[JSOI2012]分零食,洛谷P5075,有趣的公式推导和多项式求逆

正题

      题目链接点这里

      考虑一个m次多项式Q[x]=Ox^2+Sx+UQ[0]=0

      当只有一个人的时候,贡献恰好是Q[m]

      当有两个人的时候,贡献恰好是Q^2[m]

      因为第m项是由(0,m),(1,m-1),...,(m-1,1),(m,0)转移过来的,因为我们已经规定了Q[0]=0

      所以Q^2[m]恰好是两个人的贡献,以此类推。

      答案就等于\sum_{k=1}^{A} Q^k[m]

      发现是等比数列求和,在前面补上一个1,就等于(\frac{Q^{A+1}}{Q-1}-1)[m]

      小括号指的是一个多项式。

      那么Q-1直接多项式求逆就可以了,因为明显-1的逆元就是-1,所以跟P是否为质数没有关系。

      Q^{A+1}直接快速幂加FFT就可以了。

      因为m不为0,所以减不减1其实没有意义。

 

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

const int maxn=1e4,maxm=maxn*4+10;
struct complex{
	double x,y;
	complex operator+(const complex a)const{return (complex){x+a.x,y+a.y};}
	complex operator-(const complex a)const{return (complex){x-a.x,y-a.y};}
	complex operator*(const complex a)const{return (complex){x*a.x-y*a.y,x*a.y+y*a.x};}
}x[maxm],y[maxm];
int n,mod,t,a,b,c;
int last[maxn+10],temp[maxn+10],tot[maxn+10],op[maxn+10];
int limit,l;
int where[maxm];
const double Pi=acos(-1.0)*2.0;

void dft(complex *now,int idft){
	for(int i=0;i<limit;i++) if(i<where[i]) swap(now[i],now[where[i]]);
	static complex wn,w,a,b;
	for(int l=2;l<=limit;l*=2){
		wn=(complex){cos(Pi/l),idft*sin(Pi/l)};
		for(int i=0;i<limit;i+=l){
			w=(complex){1,0};
			for(int x=i,y=i+l/2;y<i+l;x++,y++,w=w*wn){
				a=now[x],b=w*now[y];
				now[x]=a+b;
				now[y]=a-b;
			}
		}
	}
}

void multi(int *a,int *b,int l1,int l2){
	limit=1,l=0;
	while(limit<l1+l2) limit*=2,l++;
	for(int i=0;i<limit;i++) 
		where[i]=((where[i>>1]>>1)|((i&1)<<(l-1))),x[i]=y[i]=(complex){0,0};
	for(int i=0;i<l1;i++) x[i]=(complex){(double)a[i],0.0};dft(x,1);
	for(int i=0;i<l2;i++) y[i]=(complex){(double)b[i],0.0};dft(y,1);
	for(int i=0;i<limit;i++) x[i]=x[i]*y[i];dft(x,-1);
	for(int i=0;i<l1;i++) a[i]=(int)(x[i].x/limit+0.5)%mod;
}

void ksm(){
	t++;
	tot[0]=1;
	while(t){
		if(t%2) multi(tot,temp,n+1,n+1);
		multi(temp,temp,n+1,n+1);
		t/=2;
	}
	(tot[0]+=mod-1)%=mod;
}

void find_ie(int *a,int now){
	if(now==1) {a[0]=mod-1;return ;}
	if(now%2==0) find_ie(a,now/2);
	else find_ie(a,now/2+1);
	for(int i=0;i<now;i++) op[i]=2*a[i]%mod;
	multi(a,a,now,now);
	multi(a,temp,now,now);
	for(int i=0;i<now;i++) a[i]=(op[i]+mod-a[i])%mod;
	return ;
}

int main(){
	scanf("%d %d %d %d %d %d",&n,&mod,&t,&a,&b,&c);
	a%=mod,b%=mod,c%=mod;t=min(n,t);
	temp[0]=last[0]=0;
	for(int i=1;i<=n;i++) temp[i]=last[i]=(a*i%mod*i%mod+b*i%mod+c)%mod;
	ksm();
	for(int i=1;i<=n;i++) temp[i]=last[i];temp[0]=mod-1;
	find_ie(last,n+1);//inverse element
	multi(tot,last,n+1,n+1);
	printf("%d",tot[n]);
}

在洛谷暂时排第四。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值