• 多项式题目


    一些符号与约定

    大写字母代表多项式,例如(F)(G)

    (f_i) 则代表多项式(F)(x^i) 项的系数

    SPOJ-SWERC14C, Golf Bot

    description

    有一个长为 (N) 的数组 (k) 与一个长为 (M) 的数组(d) ,求出 (d) 数组中能被 (le 2)(k) 数组中的数相加表示出来的数有多少个。((N,M,d_i,k_ile 2 imes 10^5))

    solution

    同底数幂的乘法是指数相加系数相乘。这启示我们将(k) 放到指数上面。具体地,构造多项式(F)(f_i=1) 当且仅当(i) 出现在(k) 数组中。注意到不一定是由恰好两个数相加得到的,于是我们可以添加(f_0=1) 表示不选择。令(G=F^2) ,不难发现(g_i>0)(i) 能被表示出来是充分必要条件。于是直接(FFT) 即可。

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef double db;
    const int N=8e5+5;const db pi=acos(-1.0),eps=1e-10;
    struct cp{db x,y;};
    inline cp operator+(const cp&x,const cp&y){return {x.x+y.x,x.y+y.y};}
    inline cp operator-(const cp&x,const cp&y){return {x.x-y.x,x.y-y.y};}
    inline cp operator*(const cp&x,const cp&y){return {x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y};}
    namespace Basis
    {
    	int rev[N];cp wn[N],f[N],g[N];
    	inline void pre(int l,int lim)
    	{
    		for(int i=0;i<lim;++i)
    			rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    	}
    	inline void FFT(cp*f,int lim,bool tp)
    	{
    		for(int i=0;i<lim;++i)if(i<rev[i])swap(f[i],f[rev[i]]);
    		for(int mid=1;mid<lim;mid<<=1)
    		{
    			wn[0]={1,0};cp g={cos(pi/mid),(tp?-1:1)*sin(pi/mid)};
    			for(int i=1;i<mid;++i)wn[i]=wn[i-1]*g;
    			int len=mid<<1;
    			for(int j=0;j<lim;j+=len)
    				for(int k=0;k<mid;++k)
    				{
    					cp x=f[j+k],y=f[j+mid+k]*wn[k];
    					f[j+k]=x+y,f[j+mid+k]=x-y;
    				}
    		}
    		if(tp)for(int i=0;i<lim;++i)f[i].x/=lim;
    	}
    	inline void mul(int n,int m,int*a,int*b)
    	{
    		int l=0,lim=1;while(lim<n+m)lim<<=1,++l;pre(l,lim);
    		for(int i=0;i<lim;++i)
    			f[i]={i<n?a[i]:0,0},g[i]={i<m?b[i]:0,0};
    		FFT(f,lim,0),FFT(g,lim,0);
    		for(int i=0;i<lim;++i)f[i]=f[i]*g[i];
    		FFT(f,lim,1);
    	}
    }
    int n,m,a[N],rub[N],cnt,mx,ans;
    int main()
    {
    	while(scanf("%d",&n)==1)
    	{
    		ans=mx=cnt=0;
    		for(int i=1,k;i<=n;++i)
    			scanf("%d",&k),a[k]=1,mx=max(rub[++cnt]=k,mx);
    		rub[++cnt]=0,a[0]=1;Basis::mul(mx+1,mx+1,a,a);
    		scanf("%d",&m);
    		for(int i=1,d;i<=m;++i)
    			scanf("%d",&d),ans+=d>2*mx?0:(Basis::f[d].x>eps);
    		printf("%d
    ",ans);
    		for(int i=1;i<=cnt;++i)a[rub[i]]=0;
    	}
    	return 0;
    }
    

    UVA12298 Super Poker II

    solution

    考虑(n=a_1+a_2+a_3+a_4) 不难发现相当于从每种花色的牌中选出一种直接组合,这和多项式乘法的意义相同。

    于是类似上一道题,我们构造(F(x)=sum_limits i[i为合数]x^i) 。如果某些牌不能选择,那么我们钦定其对应系数为(0) 。而后我们将四种花色代表的多项式乘起来得到多项式(G)(g_i) 就表示(n=i) 时的方案数,直接输出即可。

    注意此题要开long double 。

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long double db;
    typedef long long ll;
    const int N=1000000+5;const db pi=acos(-1.0),eps=1e-10;
    struct cp{db x,y;};
    inline cp operator+(const cp&x,const cp&y){return {x.x+y.x,x.y+y.y};}
    inline cp operator-(const cp&x,const cp&y){return {x.x-y.x,x.y-y.y};}
    inline cp operator*(const cp&x,const cp&y){return {x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y};}
    namespace Basis
    {
    	int rev[N];cp wn[N],f[N],g[N];
    	inline void pre(int l,int lim)
    	{
    		for(int i=0;i<lim;++i)
    			rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    	}
    	inline void FFT(cp*f,int lim,bool tp)
    	{
    		for(int i=0;i<lim;++i)if(i<rev[i])swap(f[i],f[rev[i]]);
    		for(int mid=1;mid<lim;mid<<=1)
    		{
    			wn[0]={1,0};cp g={cos(pi/mid),(tp?-1:1)*sin(pi/mid)};
    			for(int i=1;i<mid;++i)wn[i]=wn[i-1]*g;
    			int len=mid<<1;
    			for(int j=0;j<lim;j+=len)
    				for(int k=0;k<mid;++k)
    				{
    					cp x=f[j+k],y=f[j+mid+k]*wn[k];
    					f[j+k]=x+y,f[j+mid+k]=x-y;
    				}
    		}
    		if(tp)for(int i=0;i<lim;++i)f[i].x/=lim;
    	}
    	inline void mul(int n,int m,ll*a,ll*b,ll*ans)
    	{
    		int l=0,lim=1;while(lim<n+m)lim<<=1,++l;pre(l,lim);
    		for(int i=0;i<lim;++i)
    			f[i]={i<n?a[i]:0,0},g[i]={i<m?b[i]:0,0};
    		FFT(f,lim,0),FFT(g,lim,0);
    		for(int i=0;i<lim;++i)f[i]=f[i]*g[i];
    		FFT(f,lim,1);
    		for(int i=0;i<lim;++i)ans[i]=(ll)(f[i].x+0.1);
    	}
    }
    ll a[N],b[N],c[N],d[N],h[N];
    bool flag[N];int pr[N],pcnt,n,m,k;
    inline void init(int mx)
    {
    	flag[1]=1;
    	for(int i=2;i<=mx;++i)
    	{
    		if(!flag[i])pr[++pcnt]=i;
    		for(int j=1;j<=pcnt;++j)
    		{
    			int num=pr[j]*i;if(num>mx)break;
    			flag[num]=1;if(i%pr[j]==0)break;
    		}
    	}
    }
    inline int get(char*ch,int len)
    {
    	int s=0;
    	for(int i=0;i<len-1;++i)s=s*10+ch[i]-'0';
    	return s;
    }
    int main()
    {
    	init(50000);
    	char opt[10];
    	while(scanf("%d%d%d",&n,&m,&k)&&n&&m)
    	{
    		for(int i=2;i<=m;++i)if(flag[i])a[i]=b[i]=c[i]=d[i]=1;
    		for(int i=1;i<=k;++i)
    		{
    			scanf("%s",opt);
    			int len=strlen(opt),num=get(opt,len);char t=opt[len-1];
    			if(t=='S')a[num]=0;
    			else if(t=='H')b[num]=0;
    			else if(t=='C')c[num]=0;
    			else d[num]=0;
    		}
    		Basis::mul(m+1,m+1,a,b,h);
    		Basis::mul(m+1,m+1,h,c,h);
    		Basis::mul(m+1,m+1,h,d,h);
    		for(int i=n;i<=m;++i)printf("%lld
    ",h[i]);
    		puts("");
    	}
    	return 0;
    }
    

    UVA1718 Tile Cutting

    solution

    情况如图

    其中橙色平行四边形的面积为((a+b)(c+d)-ad-bc=ac+bd=s)

    注意到(s) 是定值,因此相当于我们要求(a,b,c,d) 满足(ac+bd=s) 的方案数。

    可以对于每个(i) 求出(t_i=sum_limits{x,y}[xy=s]) 。后面的就和前面的题相同了。构造多项式(F(x)=sum_limits it_ix^i) ,则(G=F^2) 的各项系数就分别对应该次数的方案数了。

    Arithmetic Progressions, CodeChef COUNTARI(bzoj3509)

    description

    给定一个长度为(n) 的数组(a) ,求有多少个(i,j,k) 满足(i<j<k)(a_k-a_j=a_j-a_i)

    data range

    (nle 10^5,a_ile 30000)

    solution

    原式可以化为

    [2a_j=a_i+a_k ]

    于是可以考虑枚举(j) ,然后统计满足(i<j<k) 和上述条件的((i,k)) 数量。

    不难想到多项式,设在(j) 左边数值(t) 出现了(c_t) 次,那么左侧的多项式为

    [f(x)=sum_{i=0}^{30000}c_ix^i ]

    右侧同理。左右两侧的多项式卷积起来后(x^{2a_j}) 的系数就是答案。

    但是每次都这样乘复杂度显然爆炸,考虑优化。

    运用分块的思想平衡复杂度,设块大小为(B)

    对于每个块,都要进行一次如上的卷积,这部分的复杂度为(mathcal O(frac nBmxlog_2mx)) 其中(mx=30000)

    然后我们需要遍历块中每一个下标作为(j) ,然后累加(j) 的所有块内贡献(块外贡献已经算出),这部分的复杂度为(mathcal O(nB))

    根据均值不等式,当(B=sqrt{mxlog_2mx}) 时最优,实际的话再微调就可以过了。

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=2e5+5;
    typedef double db;
    const db pi=acos(-1.0);
    int mx;
    struct cp{db x,y;};
    inline cp operator+(const cp&x,const cp&y){return {x.x+y.x,x.y+y.y};}
    inline cp operator-(const cp&x,const cp&y){return {x.x-y.x,x.y-y.y};}
    inline cp operator*(const cp&x,const cp&y){return {x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y};}
    namespace Basis
    {
    	int rev[N<<2],lim=1,l;cp wn[N<<2],a[N<<2],b[N<<2];ll t[N<<2];
    	inline void pre(int lim,int l)
    	{
    		for(int i=0;i<lim;++i)
    			rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    	}
    	inline void fft(int lim,cp*f,bool tp)
    	{
    		for(int i=0;i<lim;++i)
    			if(i<rev[i])swap(f[i],f[rev[i]]);
    		for(int mid=1;mid<lim;mid<<=1)
    		{
    			wn[0]={1,0};cp g={cos(pi/mid),(tp?-1:1)*sin(pi/mid)};
    			for(int i=1;i<mid;++i)wn[i]=wn[i-1]*g;
    			int len=mid<<1;
    			for(int j=0;j<lim;j+=len)
    				for(int k=0;k<mid;++k)
    				{
    					cp x=f[j+k],y=f[j+mid+k]*wn[k];
    					f[j+k]=x+y,f[j+mid+k]=x-y;
    				}
    		}
    	}
    	inline void mul(int n,int m,int*f,int*g)
    	{
    		for(int i=0;i<lim;++i)
    			a[i].x=i<n?f[i]:0,b[i].x=i<m?g[i]:0,a[i].y=b[i].y=0;
    		fft(lim,a,0),fft(lim,b,0);
    		for(int i=0;i<lim;++i)a[i]=a[i]*b[i];
    		fft(lim,a,1);
    		for(int i=0;i<lim;++i)t[i]=(ll)(a[i].x/lim+0.1);
    	}
    	inline ll query(int pos){return t[pos];}
    }
    using namespace Basis;
    int n,s[N],blk[N],lb[N],rb[N],cnt[N],f[N],g[N];ll ans;
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)scanf("%d",s+i),mx=max(mx,s[i]);
    	int k=sqrt(n*log(mx)*0.9)+1;++mx;
    	for(int i=1;i<=n;++i)blk[i]=(i-1)/k+1,++g[s[i]];
    	for(int i=1;i<=blk[n];++i)
    		lb[i]=rb[i-1]+1,rb[i]=min(k*i,n);
    	while(lim<mx+mx)lim<<=1,++l;pre(lim,l);
    	for(int i=1;i<=blk[n];++i)
    	{
    		for(int j=lb[i];j<=rb[i];++j)--g[s[j]],++cnt[s[j]];
    		mul(mx,mx,f,g);
    		for(int j=lb[i];j<=rb[i];++j)
    		{
    			int now=s[j]<<1;
    			ans+=query(now);--cnt[s[j]];
    			for(int o=lb[i];o<j;++o)
    				if(now>=s[o])ans+=cnt[now-s[o]]+g[now-s[o]];
    			for(int o=j+1;o<=rb[i];++o)
    				if(now>=s[o])ans+=f[now-s[o]];
    		}
    		for(int j=lb[i];j<=rb[i];++j)++f[s[j]];
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    Unequalled Consumption, NWERC 2005, POJ2764

    非常牛逼的一道题目呢。

    solution

    每种糖果的(OGF)

    [A_t=sum_ix^{w_ti}=frac 1{1-x^{w_t}} ]

    因此所有糖果合在一起的(OGF)

    [F=prod_t^nA_t=prod_t^nfrac 1{1-x^{w_t}} ]

    (f_i) 就表示要求总价值为(i) 时的糖果选取方案数。

    但是这个东西是个分式,直接不好做。考虑化开,令(W=lcm(w_1,w_2,cdots,w_t)) ,则

    [F=frac 1{(1-x^W)^n}prod_t^nfrac{1-x^W}{1-x^{w_t}} ]

    由等比数列求和公式(sum_limits {i=l}^rx^i=frac{1-x^{r-l+1}}{1-x}) ,因此

    [F=(sum_{i=0}x^{iW})^n imesprod_t^n(sum_{i=0}^{frac{W}{w_t}-1}x^{iw_t}) ]

    分为左右两半考虑。右边是一个多重背包:有(n) 种物品,每种物品价值为(w_t) 且有(frac{W}{{w_t}}-1) 个。这个直接暴力做就可以了。设这个多项式为(P) ,其(x^r) 的系数为(p_r) .

    再考虑左边的式子,其实只有(x^0,x^W,x^{2W},cdots) 这些位置系数是非(0) 的。而不难发现(x^{iW})其系数为(inom {n+i-1}{n-1}) ,证明可以考虑隔板法,相当于将(i) 分到(n) 个盒子中去的方案数。设这个多项式为(Q)

    现在假设我们要求总价值为(m) 时的方案数。不妨令(m=kW+r) ,那么我们有

    [G(k,r)=sum_{i=0}^{n-1}q_{(k-i)W} imes p_{iW+r}\ =sum_{i=0}^{n-1}inom{n+k-i-1}{n-1}p_{iW+r} ]

    上限为(n-1) 是因为(P) 的次数限制。

    回到原问题,我们要求最小的(m=kW+r) 满足(G(k,r)ge P) 。我们可以将(m) 按照其除以(W) 的余数进行分类,即我们每次固定(r) ,考虑所有除以(W)(r)(m) 的贡献。注意到此时(G(k,r)) 是随着(k) 增大而单调递增的,因此我们可以直接二分(可以从(G(k+1,r)-G(k,r)>0) 考虑证明这一结论)。至此问题解决。

    NO PAIN NO GAIN
  • 相关阅读:
    java-logic====吃货联盟
    jsp---》》》新闻发布系统的项目跟踪+++++++文件上传
    jsp---tomcat===》》内置对象
    JS中两个节点的关系
    HTML第二本书学习后记
    JavaScript:编程改变文本样式
    JavaS:网页中的显示和隐藏
    第一次做网页设计遇到的问题总结
    HTML中添加背景音乐
    表格的结构标记
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14672595.html
Copyright © 2020-2023  润新知