题解 P4935 【口袋里的纸飞机】

· · 题解

大家好我系出题人(还有个背锅侠mcfx)

题目背景是summer pockets!

对于测试点1,2,使用O(R^N\times N^2)的算法即可通过

对于测试点3,4,5,6,可以发现只需要枚举排序后的数列即可。然后使用一些组合数学计算答案。这样复杂度就变得可以接受。大约只有10^6种不同的数列

裸暴力的部分已经结束。现在我们来讲正解。考虑对每个数分开算贡献,即对于x\in[0,P-1],算有x出现的网格数。

考虑补集转化一下,即算没有x出现的网格数。

x=0$可以特判,即如果没有$P$的倍数出现则不会出现$0

现在只考虑x\in[1,P-1]。没有x出现,相当于有若干对(s,t)(st\mod P=x)不能同时出现在数列里。由于P是质数,所以这样的有序对一定只有(P-1)对。

比如P=5,x=1,就有如下对:(1,1),(2,3),(3,2),(4,4)

这时,你发现除去s=t的,就相当于若干个互不干涉的限制:st不能同时出现。s=t的情况就是这个数本身不能出现。这意味着这些限制可以单独考虑方案,然后再全都做卷积

你又发现R\leq 10^9。这实在是太大了!不过你可以把余数相同的视为一体,假设这个余数出现了A次,就相当于如果选这个余数,就有A种方法。

可以发现所有余数的出现次数只有AA+1这两种。所以刚才那些限制可以变成:

a0种(A,A)(即s,t都出现了A次),a1种(A,A+1),a2种(A+1,A),a3种(A+1,A+1)

所以,用指数生成函数表示,限制(A,A)的指数生成函数为e^{Ax}e^{Ax}-(e^{Ax}-1)(e^{Ax}-1)=e^{Ax}+e^{Ax}-1

答案的指数生成函数为(e^{Ax}+e^{Ax}-1)^{a0} (e^{Ax}+e^{(A+1)x}-1)^{a1+a2} (e^{(A+1)x}+e^{(A+1)x}-1)^{a3} e^{(R/P)x}(最后那个是考虑0的情况)

这已经可以快速计算,复杂度为O(n^2P+P^2)P^2是处理a0,a1,a2,a3)

打表可以发现,本质不同的(a0,a1,a2,a3)只有O(\sqrt{P})种!这不好证明,但是在数据范围下是成立的。(mcfx:可以把这个序列当做是随机的,感性理解一下)

来自出题人的辣鸡标程(该标程可以大幅优化,但是懒得写了) ```cpp // luogu-judger-enable-o2 #include<bits/stdc++.h> #define maxP 5010 #define maxn 5010 using namespace std; const int mod=1e9+7; struct data{ int a,b,c; data(){} data(int a,int b,int c):a(a),b(b),c(c){} bool operator<(const data& d)const{ if(a!=d.a)return a<d.a; if(b!=d.b)return b<d.b; return c<d.c; } }; map<data,int>st; int sP,tg[maxP]; int ans,n,P,R,fac[maxn],inv[maxn],cnt[maxP][2]; int f[maxn],g[maxn],A,lim,tmp[maxn],dp0[250][maxn],dp1[250][maxn],dp2[250][maxn]; int sdp0[250][maxn],sdp1[250][maxn],sdp2[250][maxn]; void mul(int a[],int b[],int c[]){ for(int i=0;i<=n;++i)tmp[i]=0; for(int i=0;i<=n;++i) for(int j=0;i+j<=n;++j) tmp[i+j]=(tmp[i+j]+1ll*a[i]*b[j])%mod; for(int i=0;i<=n;++i)c[i]=tmp[i]; } int qpow(int a,int b){ int ans=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1)ans=1ll*ans*a%mod; return ans; } int C(int x,int y){ if(y<0||x<y)return 0; return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod; } void cp(int fr[],int to[]){ for(int i=0;i<=n;++i)to[i]=fr[i]; } void get(int dp[250][maxn],int sdp[250][maxn],int a,int g[]){ cp(dp[a%sP],g); mul(sdp[a/sP],g,g); } void init(int dp[250][maxn],int sdp[250][maxn]){ dp[0][0]=sdp[0][0]=1; for(int i=2;i<=sP;++i)mul(dp[i-1],dp[1],dp[i]); cp(dp[sP],sdp[1]); for(int i=2;i<=sP;++i)mul(sdp[i-1],sdp[1],sdp[i]); } int main(){ scanf("%d%d%d",&n,&P,&R); // fprintf(stderr,"[R%P=%d]",R%P); fac[0]=inv[0]=1; for(int i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod; inv[n]=qpow(fac[n],mod-2); for(int i=n-1;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod; A=R/P,lim=R%P; for(int i=1;i<P;++i) for(int j=i+1;j<P;++j){ // if(i*j%P!=2)continue; if(i>lim&&j>lim)cnt[i*j%P][0]++; // 0: A else if(i<=lim&&j<=lim)cnt[i*j%P][1]++; //1:A+1 } for(int i=1;i<P;++i)tg[i*i%P]++; for(int i=1;i<P;++i) // if(i==2) st[data(cnt[i][0],cnt[i][1],tg[i])]++; sP=sqrt(P); for(int i=0;i<=n;++i)dp0[1][i]=2ll*qpow(A,i)*inv[i]%mod; dp0[1][0]--,init(dp0,sdp0); for(int i=0;i<=n;++i)dp1[1][i]=1ll*(qpow(A,i)+qpow(A+1,i))*inv[i]%mod; dp1[1][0]--,init(dp1,sdp1); for(int i=0;i<=n;++i)dp2[1][i]=2ll*qpow(A+1,i)*inv[i]%mod; dp2[1][0]--,init(dp2,sdp2); for(map<data,int>::iterator it=st.begin();it!=st.end();++it){ int a0=it->first.a,a3=it->first.b,tg=it->first.c; for(int i=0;i<=n;++i)f[i]=g[i]=0; f[0]=1; get(dp0,sdp0,a0,g),mul(f,g,f); get(dp1,sdp1,(P-1-tg)/2-a0-a3,g),mul(f,g,f); get(dp2,sdp2,a3,g),mul(f,g,f); for(int i=0;i<=n;++i)g[i]=1ll*inv[i]*qpow(A,i)%mod; mul(f,g,f); ans=(ans-1ll*it->second*(f[n]))%mod; } ans=(1ll*fac[n]*ans+1ll*P*qpow(R,n)-qpow(R-A,n))%mod; ans=(ans%mod+mod)%mod; printf("%d",ans); } ```