Loading

CF773F Test Data Generation

CF773F Test Data Generation

没能自己推出来/kk

显然,题目的限制条件就是:

  • 选择奇数个数,且总数 \(\le max_n\)

  • \(a_n\bmod 2=0,\dfrac{a_n}{g}\bmod 2=1\)

直接枚举 \(g\) 的最高的 \(2\) 的次幂可以将限制二转化为:在 \([1,\dfrac{max_a}{2^k}]\) 中选数,最大的数是奇数。

枚举最大数,枚举选的数的个数可以得到一个 \(O(na\log a)\) 的暴力(这个 \(\log\) 的来源是:每次传 a=maxa>>i 进去)

for(int i=1;i<=a;i+=2)
      for(int j=1;j<=n;j+=2)
            ans+=C[i-1][j-1];

推到这里就我推不动了/kk,一直考虑怎么算这个式子发现完全不会。。。然后只好去看题解/kk

用生成函数 \(F(a,p)=\sum f(i,a,p)z^i\)\(f(i,a,p)\)表示在 \([1,a]\) 中选数,最大数奇偶性为 \(p\) 选了 \(i\) 个的方案数,并且规定 f(0,x,y)=0,即不包括选 \(0\) 个数的情况

然后就是两个很牛逼的转移

\[\begin{cases} F(2a,p)=F(a,p)+(F(a,0)+F(a,1)+1)*F(a,p\operatorname{xor}(a\%2))\\ F(a+1,p)=F(a,p)+[(a+1)\%2=p](F(a,0)+F(a,1)+1) \end{cases} \]

怎么说呢?看到这个转移一下子就能懂,自己就是写不出来/kk

第一行:在 \([1,2a]\) 中选数,相当于只选 \(\le a\) 的,再加上,选一部分 \(\le a\) 的并且加 \(a\) ,就能出现 \(>a\) 的数了。

(如果生成函数看不习惯可以写成卷积的形式,可能更好理解)

具体来说,前 \(i\) 个数 \(\le a\) 并且随便选,后 \(n-i\) 个数 \(\le a\) 然后加 \(a\) ,同时要保证最大数的奇偶性,就有了第一行转移。妙啊!

第二行:分选 \(a+1\) 与不选 \(a+1\) 来讨论。不选就是 \(F(a,p)\) ,选 \(a+1\) 那么 \(\le a\) 的数就随便选了。

直接倍增FFT求解。但是记住 \(g\)\(2\) 的最高次幂不能是 \(0\) ,所以读进来的 \(a\) 要除以 \(2\) 。我tm写着写着忘掉了,白调了一个小时/kk

另外,这题并不用MTT,\(1e5\) 以内卷起来不会有精度问题,最大卷起来 \(1e5*1e5*3e4=3e14\) ,而且远达不到,炸不掉 double 的精度,好调好写跑得快。

虽然这题要对 \(\log a\)\(a\) 求值,但是显然,每倍增一位就统计一次答案可以随手消掉这个 \(\log a\) ,总复杂度还是倍增FFT的复杂度 \(O(n\log n\log a)\)

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define sz(v) (int)v.size()
typedef long long LL;
typedef double db;
template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
const int N=30005;
const int M=N<<2;
int n,a,mod,f[2][M],ans;
int fmod(int x){return x-=mod,x+=x>>31&mod,x;}

namespace poly{
const db PI=acos(-1);
struct cp{
	db x,y;
	cp(){x=y=0;}
	cp(db x_,db y_){x=x_,y=y_;}
	cp operator + (const cp&t)const{return cp(x+t.x,y+t.y);}
	cp operator - (const cp&t)const{return cp(x-t.x,y-t.y);}
	cp operator * (const cp&t)const{return cp(x*t.x-y*t.y,x*t.y+y*t.x);}
}w[M];
int rev[M],lim,lg;
void init(const int&n){
	for(lg=0,lim=1;lim<n;++lg,lim<<=1);
	for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1)),w[i]=cp(cos(2*PI*i/lim),sin(2*PI*i/lim));
}
void FFT(cp*a,int op){
	for(int i=0;i<lim;++i)if(i>rev[i])swap(a[i],a[rev[i]]);
	for(int i=1,t=lim>>1;i<lim;i<<=1,t>>=1){
		for(int j=0;j<lim;j+=i<<1){
			for(int k=0;k<i;++k){
				const cp X=a[j+k],Y=w[k*t]*a[i+j+k];
				a[j+k]=X+Y,a[i+j+k]=X-Y;
			}
		}
	}
	if(op)return;
	for(int i=0;i<lim;++i)a[i].x/=lim;
}
void mul(int*f,int*g,int*ans){
	static cp A[M],B[M];
	for(int i=0;i<lim;++i)A[i]=cp(f[i],0),B[i]=cp(g[i],0);
	FFT(A,1),FFT(B,1);
	for(int i=0;i<lim;++i)A[i]=A[i]*B[i],w[i].y*=-1;
	FFT(A,0);
	for(int i=0;i<lim;++i)ans[i]=LL(A[i].x+0.5)%mod;
	for(int i=0;i<lim;++i)w[i].y*=-1;
}
#define clr(a,n) memset(a,0,sizeof(int)*(n))
void shift(const int&n,const int&a){
	static int g[M],h[M],q[M];
	clr(g,lim),clr(h,lim),clr(q,lim),g[0]=1;
	for(int i=1;i<=n;++i)g[i]=fmod(f[0][i]+f[1][i]);
	mul(g,f[a&1],h),mul(g,f[(a&1)^1],q);
	for(int i=1;i<=n;++i)f[0][i]=fmod(f[0][i]+h[i]),f[1][i]=fmod(f[1][i]+q[i]);
}
void setbit(const int&n,const int&a){
	static int g[N];g[0]=1;
	for(int i=1;i<=n;++i)g[i]=fmod(f[0][i]+f[1][i]),f[1][i]=fmod(f[1][i]+g[i-1]);
}
}
signed main(){
	n=read(),a=read()>>1,mod=read();
	if(!a)return puts("0"),0;
	f[1][1]=ans=1,poly::init(n<<1);
	for(int i=log2(a)-1,len=1;i>=0;--i){
		poly::shift(n,len),len<<=1;
		if(a>>i&1)poly::setbit(n,len),++len;
		for(int j=1;j<=n;j+=2)ans=fmod(ans+f[1][j]);
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-12-29 11:36  zzctommy  阅读(210)  评论(0编辑  收藏  举报