省选测试22

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;
}
上一篇:省选测试32


下一篇:LOJ #6029. 「雅礼集训 2017 Day1」市场 线段树维护区间除法