A. 送你一道签到题
分析
大概是 \(Min25\) 筛的板子题
很久没有写了,正好复习一下
可以发现题目中给出的式子是一个积性函数
而且是 \(i^k\) 乘上一个系数的形式
所以拿 \(Min25\) 筛筛一个 \(i^k\) 就行了
系数可以用 \(dp\) 预处理
设 \(dp[i][j]\) 为当前填了第 \(i\) 个位置,已经填的幂次为 \(j\) 的方案数,每次必须填
那么幂次为 \(i\) 的系数就是 \(\sum\limits_{i}dp[j][i]C_m^j\)
预处理的时候会有一个自然数幂和的形式,可以拿伯努利数搞一下
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#define rg register
const int maxn=1e6+5,maxk=1e3+5,mod=998244353;
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
typedef long long ll;
ll n,w[maxn];
int m,k,ny[maxk],b[maxk],c[maxk][maxk];
int ksm(rg int ds,rg ll zs){
rg int nans=1;
while(zs){
if(zs&1LL) nans=mulmod(nans,ds);
ds=mulmod(ds,ds);
zs>>=1LL;
}
return nans;
}
int getmi(rg ll nn){
nn++;
rg int nans=0,tmp=nn%mod;
for(rg int i=k,now=tmp;i>=0;i--,now=mulmod(now,tmp)){
nans=addmod(nans,mulmod(c[k+1][i],mulmod(b[i],now)));
}
nans=mulmod(nans,ny[k+1]);
return nans;
}
void pre(){
ny[1]=1;
for(rg int i=2;i<=k+1;i++) ny[i]=mulmod(mod-mod/i,ny[mod%i]);
for(rg int i=0;i<=k+1;i++) c[i][0]=1;
for(rg int i=1;i<=k+1;i++){
for(rg int j=1;j<=i;j++){
c[i][j]=addmod(c[i-1][j],c[i-1][j-1]);
}
}
b[0]=1;
for(rg int i=1;i<=k+1;i++){
for(rg int j=0;j<i;j++){
b[i]=addmod(b[i],mulmod(b[j],c[i+1][j]));
}
b[i]=mulmod(b[i],mod-ny[i+1]);
}
}
bool not_pri[maxn];
int pri[maxn],sp[maxn],sqr,v1[maxn],v2[maxn],tot,g[maxn],mi[maxn];
void xxs(rg int mmax){
not_pri[0]=not_pri[1]=1;
for(rg int i=2;i<=mmax;i++){
if(!not_pri[i]){
pri[++pri[0]]=i,mi[pri[0]]=ksm(i,k);
sp[pri[0]]=addmod(sp[pri[0]-1],mi[pri[0]]);
}
for(rg int j=1;j<=pri[0] && i*pri[j]<=mmax;j++){
not_pri[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
}
int dp[maxk][maxk],xs[maxk];
int solve(rg ll x,rg int y){
if(y && x<=pri[y]) return 0;
rg int now=x<=sqr?v1[x]:v2[n/x];
rg int ans=mulmod(xs[1],delmod(g[now],sp[y]));
for(rg int i=y+1;i<=pri[0] && 1LL*pri[i]*pri[i]<=x;i++){
rg ll np=pri[i];
for(rg int j=1,tmp=mi[i];np<=x;j++,np*=pri[i],tmp=mulmod(tmp,mi[i])){
ans=addmod(ans,mulmod(mulmod(xs[j],tmp),addmod(solve(x/np,i),j!=1)));
}
}
return ans;
}
int main(){
scanf("%lld%d%d",&n,&m,&k);
pre();
sqr=sqrt(n);
xxs(sqr+100);
rg ll now;
for(rg ll l=1,r;l<=n;l=r+1){
r=(n/(n/l));
now=n/l;
w[++tot]=now;
if(now<=sqr) v1[now]=tot;
else v2[n/now]=tot;
now%=mod;
g[tot]=delmod(getmi(now),1);
}
for(rg int i=1;1LL*pri[i]*pri[i]<=n;i++){
for(rg int j=1;j<=tot && 1LL*pri[i]*pri[i]<=w[j];j++){
now=w[j]/pri[i];
now=now<=sqr?v1[now]:v2[n/now];
g[j]=delmod(g[j],mulmod(mi[i],delmod(g[now],sp[i-1])));
}
}
dp[0][0]=1;
for(rg int i=1;i<=40;i++){
for(rg int j=0;j<=40;j++){
for(rg int o=j+1;o<=40;o++){
dp[i][o]=addmod(dp[i][o],mulmod(dp[i-1][j],o-j+1));
}
}
}
for(rg int i=1;i<=40;i++){
for(rg int j=1,tmp=m;j<=40;j++){
xs[i]=addmod(xs[i],mulmod(dp[j][i],tmp));
tmp=mulmod(tmp,mulmod(ny[j+1],m-j));
}
}
printf("%d\n",addmod(solve(n,0),1));
return 0;
}
B. 神犇
分析
看到异或和可以想到差分和 \(01trie\)
记录一下 \(0,1,2\) 出现次数的前缀和,分别设为 \(sum0,sum1,sum2\)
对于一个 \(r\),查询最大的 \(sumxor[r]\ xor\ sumxor[l]\)
并且满足 \(sum0[r]-sum0[l] \neq sum1[r]-sum1[l] \neq sum2[r]-sum2[l]\)
后面的三个式子移项之后就只和 \(l\) 或者 \(r\)有关
也就是找到一个 \(l\)
使得 \(sum1[l]-sum0[l] \ne sum1[r]-sum0[r]\)
\(sum2[l]-sum1[l] \neq sum2[r]-sum1[r]\)
\(sum0[l]-sum2[l] \neq sum0[r]-sum2[r]\)
把这三个差分别设成 \(val1,val2,val3\)
就是要 \(val1[l] \neq val1[r],val2[l] \neq val2[r],val3[l] \neq val3[r]\)
而且 \(sumxor[r]\ xor\ sumxor[l]\) 最大
记录 \(trie\) 树上每一个节点出现的次数
容斥一下
那么答案就是总的- \(a\)相等的 - \(b\) 相等的- \(c\) 相等+\(ab\) 都相等的+\(ac\) 都相等的+\(bc\)都相等的-\(abc\)都相等的
然后会发现 \(ab\) 相等就等价于 \(abc\) 相等 \(,\) 同理
也就是后面的 \(4\) 个可以合成一个
最终的答案就是 \(all-a-b-c+2abc\)
对于这 \(5\) 种 \(trie\) 树的每一个值都开一棵 \(trie\) 树
最后空间大概是 \(400MiB\)
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<set>
#include<vector>
#include<map>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
typedef unsigned long long ull;
const int maxn=3e5+5;
std::map<ull,int> mp1;
std::map<int,int> mp2[3];
int n,a[maxn],op,latans,p[maxn],totrt;
int tr[maxn*120][2],cnt,sum0[maxn],sum1[maxn],sum2[maxn],sumxor[maxn],siz[maxn*120];
void ad(rg int da,rg int val){
for(rg int i=31;i>=1;i--){
rg int p=(val>>(i-1))&1;
if(!tr[da][p]) tr[da][p]=++cnt;
da=tr[da][p];
siz[da]++;
}
}
int cx(rg int rt1,rg int rt2,rg int rt3,rg int rt4,rg int rt5,rg int val){
rg int nans=0;
for(rg int i=31;i>=1;i--){
rg int p=(val>>(i-1))&1;
rg int tmp=siz[tr[rt1][p^1]]-siz[tr[rt2][p^1]]-siz[tr[rt3][p^1]]-siz[tr[rt4][p^1]]+2*siz[tr[rt5][p^1]];
if(tmp){
rt1=tr[rt1][p^1],rt2=tr[rt2][p^1],rt3=tr[rt3][p^1],rt4=tr[rt4][p^1],rt5=tr[rt5][p^1];
nans+=(1<<(i-1));
} else {
rt1=tr[rt1][p],rt2=tr[rt2][p],rt3=tr[rt3][p],rt4=tr[rt4][p],rt5=tr[rt5][p];
}
}
return nans;
}
int main(){
n=read(),op=read();
for(rg int i=1;i<=n;i++) p[i]=read();
for(rg int i=1;i<=n;i++) a[i]=read();
rg int op1,op2,op3;
for(rg int i=1;i<=n;i++){
if(op) p[i]=(latans^p[i])%3;
if(op) a[i]=latans^a[i];
sum0[i]=sum0[i-1]+(p[i]==0);
sum1[i]=sum1[i-1]+(p[i]==1);
sum2[i]=sum2[i-1]+(p[i]==2);
sumxor[i]=sumxor[i-1]^a[i];
op1=sum0[i-1]-sum1[i-1],op2=sum1[i-1]-sum2[i-1],op3=sum2[i-1]-sum0[i-1];
if(!totrt) totrt=++cnt;
ad(totrt,sumxor[i-1]);
if(!mp2[0][op1]) mp2[0][op1]=++cnt;
ad(mp2[0][op1],sumxor[i-1]);
if(!mp2[1][op2]) mp2[1][op2]=++cnt;
ad(mp2[1][op2],sumxor[i-1]);
if(!mp2[2][op3]) mp2[2][op3]=++cnt;
ad(mp2[2][op3],sumxor[i-1]);
if(!mp1[(unsigned long long)op1*n*n+op2*n+op3]) mp1[(unsigned long long)op1*n*n+op2*n+op3]=++cnt;
ad(mp1[(unsigned long long)op1*n*n+op2*n+op3],sumxor[i-1]);
op1=sum0[i]-sum1[i],op2=sum1[i]-sum2[i],op3=sum2[i]-sum0[i];
latans=cx(totrt,mp2[0][op1],mp2[1][op2],mp2[2][op3],mp1[(unsigned long long)op1*n*n+op2*n+op3],sumxor[i]);
printf("%d ",latans);
}
printf("\n");
return 0;
}
C. 开挂
分析
枚举含有 \(1\) 的矩形的大小
为了避免重复,要满足矩形的四个边界上都有 \(1\)
最优的方案肯定是选左下右上或者左上右下
把四个边界和不包含在哪种方案中状压一下即可
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=65,maxm=75,mod=998244353;
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
int n,m,ans=1,x,y,f[2][maxm];
int getzt(rg int mx,rg int my,rg int nx,rg int ny){
rg int nzt=0;
if(mx==1) nzt|=1;
if(mx==nx) nzt|=2;
if(my==1) nzt|=4;
if(my==ny) nzt|=8;
if(!(mx<=x && my<=y) && !(nx-mx+1<=x && ny-my+1<=y)) nzt|=16;
if(!(nx-mx+1<=x && my<=y) && !(mx<=x && ny-my+1<=y)) nzt|=32;
return nzt;
}
int solve(rg int nx,rg int ny){
rg int now=1;
memset(f,0,sizeof(f));
f[1][getzt(1,1,nx,ny)]=f[1][0]=1;
for(rg int i=1;i<=nx;i++){
for(rg int j=1;j<=ny;j++){
if(i==1 && j==1) continue;
now^=1;
memset(f[now],0,sizeof(f[now]));
rg int tmp=getzt(i,j,nx,ny);
for(rg int k=0;k<=63;k++){
f[now][k]=addmod(f[now][k],f[now^1][k]);
f[now][k|tmp]=addmod(f[now][k|tmp],f[now^1][k]);
}
}
}
rg int nans=addmod(addmod(f[now][31],f[now][47]),f[now][15]);
return nans;
}
int main(){
n=read(),m=read(),x=read(),y=read();
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=m;j++){
ans=addmod(ans,mulmod(solve(i,j),mulmod(n-i+1,m-j+1)));
}
}
printf("%d\n",ans);
return 0;
}