• 6.3 考试修改+总结


    今天下午考试被FFT和数论题目翔了一脸QAQ

    做的是Newscafe杯的题目

    第一题 异化多肽

    显然构造多项式f

    答案是f+f^2+f^3……

    化简一下得1/(1-f)

    之后多项式求逆即可

    考试的时候推了好久的多项式求逆的式子(感觉自己什么都忘光了

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<cstdlib>
    #include<algorithm>
    #define G 5
    using namespace std;
    
    typedef long long LL;
    const int maxn=300010;
    const int mod=1005060097;
    int n,m,k,N,L;
    int A[maxn],B[maxn];
    int rev[maxn];
    
    void read(int &num){
    	num=0;char ch=getchar();
    	while(ch<'!')ch=getchar();
    	while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
    }
    int pow_mod(int v,int p){
    	int tmp=1;
    	while(p){
    		if(p&1)tmp=1LL*tmp*v%mod;
    		v=1LL*v*v%mod;p>>=1;
    	}return tmp;
    }
    void FFT(int *A,int n,int flag){
    	for(L=0;(1<<L)<n;++L);
    	for(int i=0;i<n;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(L-1));
    	for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
    	for(int k=2;k<=n;k<<=1){
    		int mk=(k>>1);
    		int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
    		for(int i=0;i<n;i+=k){
    			int w=1;
    			for(int j=0;j<mk;++j){
    				int x=A[i+j],y=1LL*A[i+j+mk]*w%mod;
    				A[i+j]=x+y;if(A[i+j]>=mod)A[i+j]-=mod;
    				A[i+j+mk]=x-y;if(A[i+j+mk]<0)A[i+j+mk]+=mod;
    				w=1LL*w*wn%mod;
    			}
    		}
    	}
    	if(flag==-1){
    		int inv=pow_mod(n,mod-2);
    		for(int i=0;i<n;++i)A[i]=1LL*A[i]*inv%mod;
    	}return;
    }
    void Get_inv(int n){
    	if(n==1){
    		B[0]=pow_mod(A[0],mod-2);
    		return;
    	}
    	int now=(n>>1);
    	Get_inv(now);
    	static int tmp[maxn];
    	for(int i=0;i<now;++i)tmp[i]=A[i];
    	FFT(tmp,n,1);FFT(B,n,1);
    	for(int i=0;i<n;++i)B[i]=1LL*B[i]*(2-1LL*tmp[i]*B[i]%mod+mod)%mod;
    	FFT(B,n,-1);
    	memset(B+now,0,sizeof(int)*now);
    }
    
    int main(){
    	freopen("polypeptide.in","r",stdin);
    	freopen("polypeptide.out","w",stdout);
    	read(n);read(m);
    	for(int i=1;i<=m;++i){
    		read(k);
    		if(k<=n)A[k]++;
    	}A[0]--;if(A[0]<0)A[0]+=mod;
    	for(N=1;N<=n;N<<=1);N<<=1;
    	Get_inv(N);
    	int ans=-B[n];
    	ans=(ans%mod+mod)%mod;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    第二题正解是用圆周卷积优化计算过程,也要写FFT

    但是考试的时候没有看出来,于是就手写了一发bitset

    结果压位大法好,暴力出奇迹

    开O2的情况A掉了全部的数据QAQ

    然而至今不能看懂题解

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define fastcall __attribute__((optimize("-O3")))
    #define IL __inline__ __attribute__((always_inline))
    using namespace std;
    
    typedef long long LL;
    const int maxn=100010;
    int m,n,blo,k,lim,QAQ,TAT;
    int ans=0x7fffffff;
    char s[maxn];
    LL A[maxn],B[maxn],S;
    int L[maxn],R[maxn];
    int Ans[maxn];
    int Num[2000010];
    int Q[maxn];
    
    fastcall IL int Get_Num(LL v){
    	int sum=Num[v&lim];v>>=20;
    	sum+=Num[v&lim];v>>=20;
    	sum+=Num[v&lim];return sum;
    }
    fastcall IL void check(int S){
    	int sum=Get_Num(S);
    	int A=(S>>(m+1)&1),B=(S>>m&1);B^=1;
    	int tmp=(S&QAQ);
    	int now=(2*B-1)*tmp%n;
    	if(now<0)now+=n;
    	if(A==0)sum+=Ans[now];
    	else sum+=(n-Ans[now]);
    	if(sum<ans)ans=sum;
    }
    
    int main(){
    	freopen("virus.in","r",stdin);
    	freopen("virus.out","w",stdout);
    	scanf("%d%d",&m,&n);blo=60;
    	k=(n%blo==0?n/blo:n/blo+1);lim=(1<<20)-1;QAQ=(1<<m)-1;
    	for(int i=0;i<(1<<20);++i)Num[i]=Num[i>>1]+(i&1);
    	scanf("%s",s+1);
    	for(int i=1;i<=k;++i)L[i]=(i-1)*blo+1,R[i]=min(n,i*blo);
    	for(int i=1;i<=k;++i){
    		for(int j=L[i];j<=R[i];++j)A[i]=A[i]<<1|(s[j]-'0');
    	}
    	scanf("%s",s+1);
    	for(int i=1;i<=k;++i){
    		for(int j=L[i];j<=R[i];++j)B[i]=B[i]<<1|(s[j]-'0');
    	}
    	for(int i=0;i<n;++i){
    		int now=0;
    		for(int j=1;j<=k;++j)now+=Get_Num(A[j]^B[j]);
    		Ans[i]=now;now=(A[k]&1);
    		for(int j=1;j<=k;++j){
    			int tmp=(A[j]&1);
    			int len=R[j]-L[j];
    			A[j]>>=1;
    			if(now)A[j]|=(1LL<<len);
    			now=tmp;
    		}
    	}
    	int up=(1<<(m+2));
    	for(int i=0;i<up;++i)check(i);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    第三题是个数论题

    推导的式子很长

    最后推导的结论是R(i)+i是积性的

    然后就很好搞了QAQ

    至于式子嘛,明天再补上(挖坑ing)(其实是因为并不会用编辑器写公式)

    吐槽一下std,花式整数溢出啊,导致造的数据都是错的

    考试的时候没有看出那个性质,于是只能写O(n^2)的暴力

    正准备分块打表的时候吕老师问我做完了没有,我想了想反正分块打表也没有什么意思

    就直接拿到数据测了QAQ

    如果分块打表的话这道题目是50分QAQ直接暴力是20分

    (然而正确的数据其实只有5个)

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    using namespace std;
    
    const int maxn=1000010;
    const int mod=1005060097;
    typedef long long LL;
    LL n,ans,inv;
    int mu[maxn];
    int p[maxn],cnt=0;
    bool vis[maxn];
    
    LL pow_mod(LL v,int p){
    	LL tmp=1;
    	while(p){
    		if(p&1)tmp=tmp*v%mod;
    		v=v*v%mod;p>>=1;
    	}return tmp;
    }
    void Get_Prime(){
    	mu[1]=1;
    	for(int i=2;i<=1000000;++i){
    		if(!vis[i]){
    			p[++cnt]=i;
    			mu[i]=-1;
    		}
    		for(int j=1;j<=cnt;++j){
    			if(1LL*i*p[j]>1000000)break;
    			int ip=i*p[j];
    			vis[ip]=true;
    			if(i%p[j]==0)break;
    			mu[ip]=-mu[i];
    		}
    	}return;
    }
    LL Get_mod(LL n){return n%mod;}
    LL Get_ans(LL n){
    	LL la,sum=0;
    	for(LL i=1;i<=n;i=la+1){
    		LL now=n/i;
    		la=n/now;
    		sum=sum+Get_mod(la+i)*Get_mod(la-i+1)%mod*inv%mod*Get_mod(n/i)%mod;
    		if(sum>=mod)sum-=mod;
    	}return sum;
    }
    
    int main(){
    	freopen("function.in","r",stdin);
    	freopen("function.out","w",stdout);
    	scanf("%lld",&n);
    	Get_Prime();inv=pow_mod(2LL,mod-2);
    	int lim=(int)(sqrt(n));
    	for(int i=1;i<=lim;++i){
    		ans=ans+1LL*mu[i]*i*Get_ans(n/i/i)%mod;
    		if(ans<0)ans+=mod;
    		if(ans>=mod)ans-=mod;
    	}
    	ans=ans-Get_mod(n)*Get_mod(n+1)%mod*inv%mod;
    	if(ans<0)ans+=mod;
    	if(ans>=mod)ans-=mod;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    明天就要去thusc报道了

    希望自己能有个好成绩吧

    感觉自己还是学的太少,写的太少,想的太少

    以后不能继续颓下去了QAQ

    UPD:感觉自己还是太naive了

    第二题想了想发现是一道很简单的题目QAQ FFT的优化也很容易理解

    第三题的式子就一直坑着吧,实在是不会用编辑器写公式QAQ

    算了,贴一发题解吧

    然后有了式子就随便搞了QAQ

    UPD:第二题在去T大的火车上突然想出来了

    我们考虑时间复杂度的瓶颈在于计算右移后两个串异或后1的个数

    不妨考虑计算两个串对应位上都是1的个数

    不难发现将第一个串反序之后对两个串做FFT

    可以计算对应为都是1的个数,而当我们把第一个串倍长之后

    一遍FFT就可以计算右移0-(n-1)位的所有答案

    之后我们把两个串都取反再做一次,可以统计对应位都是0的个数

    最后枚举选择方案O(1)统计答案即可

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #define G 3
    using namespace std;
    
    const int mod=998244353;
    const int maxn=300010;
    int m,n,L,N,QAQ,ans=0x7fffffff;
    char a[maxn],b[maxn];
    int A[maxn],B[maxn],C[maxn];
    int Ans[maxn],rev[maxn];
    int Num[20000010];
    
    int pow_mod(int v,int p){
    	int tmp=1;
    	while(p){
    		if(p&1)tmp=1LL*tmp*v%mod;
    		v=1LL*v*v%mod;p>>=1;
    	}return tmp;
    }
    void FFT(int *A,int n,int flag){
    	for(int i=0;i<n;++i)C[i]=A[rev[i]];
    	for(int i=0;i<n;++i)A[i]=C[i];
    	for(int k=2;k<=n;k<<=1){
    		int mk=(k>>1);
    		int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
    		for(int i=0;i<n;i+=k){
    			int w=1;
    			for(int j=0;j<mk;++j){
    				int x=A[i+j],y=1LL*A[i+j+mk]*w%mod;
    				A[i+j]=x+y;if(A[i+j]>=mod)A[i+j]-=mod;
    				A[i+j+mk]=x-y;if(A[i+j+mk]<0)A[i+j+mk]+=mod;
    				w=1LL*w*wn%mod;
    			}
    		}
    	}
    	if(flag==-1){
    		int inv=pow_mod(n,mod-2);
    		for(int i=0;i<n;++i)A[i]=1LL*A[i]*inv%mod;
    	}return;
    }
    void Get_flip(){
    	int lim=(n>>1);
    	for(int i=1;i<=lim;++i)swap(a[i],a[n-i+1]);
    }
    void Get_rev(){
    	for(int i=1;i<=n;++i){
    		if(a[i]=='1')a[i]='0';
    		else a[i]='1';
    	}
    	for(int i=1;i<=n;++i){
    		if(b[i]=='1')b[i]='0';
    		else b[i]='1';
    	}return;
    }
    void Get_FFT(){
    	memset(A,0,sizeof(A));memset(B,0,sizeof(B));
    	for(int i=1;i<=n;++i)A[i]=a[i]-'0';
    	for(int i=n+1;i<=(n<<1);++i)A[i]=A[i-n];
    	for(int i=1;i<=n;++i)B[i]=b[i]-'0';
    	FFT(A,N,1);FFT(B,N,1);
    	for(int i=0;i<N;++i)A[i]=1LL*A[i]*B[i]%mod;
    	FFT(A,N,-1);
    	for(int i=n+1;i<=(n<<1);++i)Ans[i-n-1]+=A[i];
    }
    void check(int S){
    	int sum=Num[S];
    	int A=(S>>(m+1)&1),B=(S>>m&1);B^=1;
    	int tmp=(S&QAQ);
    	int now=(2*B-1)*tmp%n;
    	if(now<0)now+=n;
    	if(A==0)sum+=(n-Ans[now]);
    	else sum+=Ans[now];
    	if(sum<ans)ans=sum;
    }
    
    int main(){
    	freopen("virusb.in","r",stdin);
    	freopen("virusb.out","w",stdout);
    	scanf("%d%d",&m,&n);QAQ=(1<<m)-1;
    	scanf("%s",a+1);scanf("%s",b+1);
    	for(N=1,L=0;N<=(n<<1);N<<=1,L++);
    	for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(L-1));
    	Get_flip();Get_FFT();
    	//for(int i=0;i<n;++i)printf("%d ",Ans[i]);
    	//printf("
    "); 
    	Get_rev();Get_FFT();
    	//for(int i=0;i<n;++i)printf("%d ",Ans[i]);
    	//printf("
    ");
    	int up=(1<<(m+2));
    	for(int i=0;i<up;++i)Num[i]=Num[i>>1]+(i&1);
    	for(int i=0;i<up;++i)check(i);
    	printf("%d
    ",ans);
    	return 0;
    } 
    

      

  • 相关阅读:
    努力学习吧!
    C# 捕捉键盘事件
    oracle 11g 及 plsqldeveloper 相关操作
    Oracle 建表空间
    窗体程序 防止重复打开子窗体
    asp 下 ewebeditor 上传图片功能,在IE7,IE8 及更高版本上失效解决方法
    StringBuilder 在后台动态输出 html 代码
    起动停止 Oracle11g 三个服务的批处理写法
    MySQL 常用命令语句
    虚拟机—pychrm
  • 原文地址:https://www.cnblogs.com/joyouth/p/5557686.html
Copyright © 2020-2023  润新知