• 省选测试22


    A. 送你一道签到题

    分析

    大概是 (Min25) 筛的板子题

    很久没有写了,正好复习一下

    可以发现题目中给出的式子是一个积性函数

    而且是 (i^k) 乘上一个系数的形式

    所以拿 (Min25) 筛筛一个 (i^k) 就行了

    系数可以用 (dp) 预处理

    (dp[i][j]) 为当前填了第 (i) 个位置,已经填的幂次为 (j) 的方案数,每次必须填

    那么幂次为 (i) 的系数就是 (sumlimits_{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
    ",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] eq sum1[r]-sum1[l] eq sum2[r]-sum2[l])

    后面的三个式子移项之后就只和 (l) 或者 (r)有关

    也就是找到一个 (l)

    使得 (sum1[l]-sum0[l] e sum1[r]-sum0[r])

    (sum2[l]-sum1[l] eq sum2[r]-sum1[r])

    (sum0[l]-sum2[l] eq sum0[r]-sum2[r])

    把这三个差分别设成 (val1,val2,val3)

    就是要 (val1[l] eq val1[r],val2[l] eq val2[r],val3[l] eq 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("
    ");
    	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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Google的Java常用类库 Guava资料
    Java 理论与实践: 哈希
    7 款开源 Java 反编译工具
    Eclipse传递main函数参数
    Java程序员常用工具类库
    Eclipse 安装插件
    学习Javascript的8张思维导图
    java开发者最常去的20个英文网站
    关于工作效率的心得分享
    ProtoBuf开发者指南
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14409540.html
Copyright © 2020-2023  润新知