20190730
互质对
题意简述
给你一个长度为 $n$ 的序列。$q$ 次操作,每次将一个数删除或加入,问操作完后的序列互质对个数。
$n,q\leq 10^5,a_i\leq 5\times 10^5$ 。
$solution:$
对于数 $x\leq 5\times 10^5$ ,它的质因子个数不超过 $7$ 个,所以直接做简单容斥即可。
时间复杂度 $O(n\sqrt{a})$ 。
写法优秀的可以为 $O(n\times 2^7)$
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=510001; int n,q,cnt,g[MAXN],num[MAXN],M[MAXN]; inline void dfs(int ps,int res,int ans){ if(ps==g[0]+1){ if(res==0) return; if(res&1) cnt+=num[ans]; else cnt-=num[ans]; return; } dfs(ps+1,res+1,ans*g[ps]); dfs(ps+1,res,ans); return; } inline int get(int x){ g[0]=0; cnt=0; for(register int i=2;i<=sqrt(x);++i){ if(x%i==0) g[++g[0]]=i; while(x%i==0) x/=i; } if(x!=1) g[++g[0]]=x; dfs(1,0,1); return cnt; } inline void Modify(int x,int opt){ for(register int i=1;i<sqrt(x);++i){ if(x%i==0) num[i]+=opt,num[x/i]+=opt; } if((int)((int)sqrt(x)*(int)sqrt(x))==x) num[(int)sqrt(x)]+=opt; return; } int a[MAXN],Ans,Num,vis[MAXN]; signed main(){ freopen("coprime.in","r",stdin); freopen("coprime.out","w",stdout); n=read(),q=read(); for(register int i=1;i<=n;i++) a[i]=read(); for(register int i=1;i<=q;i++){ int x=read(); vis[x]^=1; if(vis[x]==1){ if(a[x]==1) Ans+=Num; else Ans+=(Num-get(a[x])); Modify(a[x],1); Num++; }else{ if(a[x]==1) Ans-=(Num-1); else Ans-=(Num-get(a[x])); Modify(a[x],-1); Num--; } printf("%lld\n",Ans); }return 0; }
斐波那契
题意简述
$T$ 组询问,每次询问 $(F_n\mod F_k)\mod 10^9+7$ 。$F_i$ 表示斐波那切数列第 $i$ 项。
$T\leq 5\times 10^4,n,k\leq 10^{18}$ 。
$solution:$
考虑对于 $F_k$ 递推得到 $F_n$ 。
$F_{k-1}=F_1\times F_{k-1}+F_0\times F_k,F_k=F_0\times F_{k-1}+F_1\times F_k$ 。
通过 $F_i=F_{i-2}+F_{i-1}$ 可以得到 $F_n=F_{n-k}\times F_{k-1}+F_{n-k+1}\times F_k$ 。
下面公式省略 $\mod f_k$
则 $$F_n=F_{n-k}\times F_{k-1}+F_{n-k+1}\times F_k\\\equiv F_{n-k}\times F_{k-1}$$
对于 $F_{n-k}$ 依然可以迭代,设 $p=[\dfrac{n}{k}],q=n\mod k$ 。
$$F_n\equiv f_{k-1}^p\times f_q$$
通过找规律发现 $$F_{k-1}^2+F_{k-1}\times F_k-F_k^2=(-1)^k$$
推出 $$F_{k-1}^2\equiv (-1)^k$$
发现答案与 $p$ 的奇偶有关。
当 $p$ 为偶数时,$$F_n\equiv[(-1)^k]^{\frac{p}{2}}\times F_q$$
当 $p$ 为奇数时,我们发现无法去表达出对 $f_k$ 取模的值,考虑将 $i$ 值补为偶数。
将斐波那切数列拓展到负域中,$f_{-i}=(-1)^{i+1}\times f_i$ ,所以$$F_n\equiv F_{k-1}^{p+1}\times F_{q-k}\\\equiv (-1)^{k-q+1}\times F_{k-1}^{p+1}\times F_{k-q}$$
然后就同 $p$ 为偶数处理即可。
每次只要用矩阵快速幂快速求出 $F$ 即可。
时间复杂度 $O(T\log k)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } struct Matrix{ int a[3][3]; void clear(){memset(a,0,sizeof(a));} }F,G; Matrix operator*(Matrix x1,Matrix x2){ Matrix x3;x3.clear(); for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) for(int k=1;k<=2;k++) x3.a[i][j]+=x1.a[i][k]*x2.a[k][j],x3.a[i][j]%=mod; return x3; } Matrix ksm(Matrix a,int b){ Matrix ans;ans.clear(); for(int i=1;i<=2;i++) ans.a[i][i]=1; while(b){ if(b&1) ans=ans*a; a=a*a,b>>=1; }return ans; } int Qfib(int ps){ if(ps==0) return 0; if(ps==1) return 1; F.clear(),G.clear(); F.a[1][1]=F.a[1][2]=F.a[2][1]=1; G=ksm(F,ps-1); return G.a[1][1]; } int Mod(int x){return ((x%mod)+mod)%mod;} int T,n,k; void solve(){ n=read(),k=read(); if(k<=2){printf("0\n");return;} if(n<k){printf("%lld\n",Qfib(n));return ;} int p=n/k,q=n%k; if(q==0){printf("%d\n",0);return;} if(p%2==0){ int opt=((k*(p/2))&1?-1:1); if(Qfib(q)==0){printf("%lld\n",0);return;} if(opt==1){printf("%lld\n",Qfib(q));return;} printf("%lld\n",Mod(Qfib(k)-Qfib(q))); return; } p++; int opt=(((k*(p/2)&1))?-1:1)*(((k-q+1)&1)?-1:1); if(opt==1){printf("%lld\n",Qfib(k-q));return;} else {printf("%lld\n",Mod(Qfib(k)-Qfib(k-q)));return;} return; } signed main(){ // freopen("make.in","r",stdin); freopen("fib.in","r",stdin); freopen("fib.out","w",stdout); T=read(); while(T--) solve(); return 0; }
不连续回文串
题意简述
给定一个长度为 $n$ 的 $01$ 字符串。
求出满足位置和字符均对于某条对称轴对称的不连续回文子串。
答案对 $10^9+7$ 取模。
$n\leq 10^5$ 。
$solution:$
先不考虑不连续这个限制,最后可以直接二分加哈希求出。
如果 $l,r$ 会对于 $x$ 这个位置作贡献,必须满足 $a_l=a_r,2x=l+r$ 。
直接对于 $0,1$ 分别卷积后快速幂求出对数即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long #define mod 1004535809 #define Mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=400001; int n,m,flip[MAXN],f[MAXN],g[MAXN]; int ksm(int a,int b,int MOd){ int ans=1; while(b){ if(b&1) ans*=a,ans%=MOd; a*=a,a%=MOd; b>>=1; }return ans; } inline void NTT(int *f,int opt){ for(int i=0;i<n;i++) if(i<flip[i]) swap(f[i],f[flip[i]]); for(int p=2;p<=n;p<<=1){ int len=p>>1,buf=ksm(3,(mod-1)/p,mod); if(opt==-1) buf=ksm(buf,mod-2,mod); for(int be=0;be<n;be+=p){ int tmp=1; for(int l=be;l<be+len;l++){ int t=f[l+len]*tmp;t%=mod; f[len+l]=(f[l]-t+mod)%mod,f[l]=(f[l]+t)%mod; tmp*=buf,tmp%=mod; } } }if(opt==-1){ int Inv=ksm(n,mod-2,mod); for(int i=0;i<n;i++) f[i]*=Inv,f[i]%=mod; }return; } char str[MAXN]; int len,a[MAXN]; inline void work(int opt){ memset(f,0,sizeof(f)),memset(g,0,sizeof(g)); n=len,m=len; for(int i=1;i<=len;i++) if(a[i]==opt) f[i]=g[i]=1; m+=n; for(n=1;n<=m;n<<=1); for(int i=0;i<n;i++) flip[i]=(flip[i>>1]>>1)|(i&1?n>>1:0); NTT(f,1),NTT(g,1); for(int i=0;i<n;i++) f[i]*=g[i],f[i]%=mod; NTT(f,-1); return; } int ans[MAXN],Ans,pw[MAXN],Hash1[MAXN],Hash2[MAXN],Cnt; inline int hash1(int l,int r){return Hash1[r]-Hash1[l-1]*pw[r-l+1];} inline int hash2(int l,int r){return Hash2[l]-Hash2[r+1]*pw[r-l+1];} inline int solve(){ n=len; pw[0]=1;for(int i=1;i<=n;i++) pw[i]=pw[i-1]*3; for(int i=1;i<=n;i++) Hash1[i]=Hash1[i-1]*3+a[i]; for(int i=n;i>=1;i--) Hash2[i]=Hash2[i+1]*3+a[i]; for(int i=1;i<=n;i++){ int l=0,r=min(i-1,n-i),Maxn=INT_MIN; while(l<=r){ int mid=l+r>>1; if(hash1(i-mid,i)==hash2(i,i+mid)){Maxn=max(Maxn,mid);l=mid+1;} else r=mid-1; } Cnt+=Maxn+1; } for(int i=1;i<n;i++){ int l=1,r=min(i,n-i),Maxn=0; while(l<=r){ int mid=l+r>>1; if(hash1(i-mid+1,i)==hash2(i+1,i+mid)){Maxn=max(Maxn,mid);l=mid+1;} else r=mid-1; } Cnt+=Maxn; }return Cnt; } signed main(){ freopen("palindrome.in","r",stdin); freopen("palindrome.out","w",stdout); scanf("%s",str+1); len=strlen(str+1); for(int i=1;i<=len;i++) a[i]=str[i]-'0'; work(0); for(int i=2;i<=2*len;i++) ans[i]+=f[i],ans[i]%=Mod; work(1); for(int i=2;i<=2*len;i++) ans[i]+=f[i],ans[i]%=Mod; int Inv=ksm(2,Mod-2,Mod); for(int i=2;i<=2*len;i++){ if(i%2==0){ Ans+=ksm(2,((ans[i]+1)*Inv)%Mod,Mod); Ans--;Ans=((Ans%Mod)+Mod)%Mod; }else{ Ans+=ksm(2,(ans[i]*Inv)%Mod,Mod); Ans--;Ans=((Ans%Mod)+Mod)%Mod; } }printf("%lld\n",(((Ans-solve())%Mod)+Mod)%Mod);return 0; }