洛谷P2000 拯救世界(NTT+生成函数)

题目链接
这题还是蛮不错的,去年noip之前就推出了通项,但因为FFT被卡了精度就一直没写
和那道食物差不多的推式子方法
kkksc03大神召唤方法:
金神石的块数必须是6的倍数。
1 + x 6 + x 12 + . . . = 1 x 6 1 1+x^6+x^{12}+...=\frac{1}{x^6-1}
木神石最多用9块。
1 + x + x 2 + . . . + x 9 = x 10 1 x 1 1+x+x^2+...+x^9=\frac{x^{10}-1}{x-1}
水神石最多用5块。
x 6 1 x 1 \frac{x^6-1}{x-1}
火神石的块数必须是4的倍数。
1 x 4 1 \frac{1}{x^4-1}
土神石最多用7块。
x 8 1 x 1 \frac{x^8-1}{x-1}
lzn大神召唤方法:
金神石的块数必须是2的倍数。
1 x 2 1 \frac{1}{x^2-1}
木神石最多用1块。
x 2 1 x 1 \frac{x^2-1}{x-1}
水神石的块数必须是8的倍数。
1 x 8 1 \frac{1}{x^8-1}
火神石的块数必须是10的倍数。
1 x 10 1 \frac{1}{x^{10}-1}
土神石最多用3块。
x 4 1 x 1 \frac{x^4-1}{x-1}

反正一乘就会发现消掉了一堆,得到了
1 ( 1 x ) 5 \frac{1}{(1-x)^5}

广义二项式定理一下就是 c n + 5 1 5 1 = ( n + 1 ) ( n + 2 ) ( n + 3 ) ( n + 4 ) 24 c^{5-1}_{n+5-1}=\frac{(n+1)(n+2)(n+3)(n+4)}{24}

因为长度很长所以要高精度,用NTT搞下就可以了

代码如下:(一如既往的难看

#include<bits/stdc++.h>
#define mod 998244353
#define g 3ll
using namespace std;

char s[100010];

long long kasumi(long long a,long long b)
{
	long long ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

long long n1[600040],n2[600040],n3[600040],n4[600040],ans[600040];
long long len1,len2,len3,len4;
long long limit=1,r[600040];

long long NTT(long long *a,long long kd)
{
	for(long long i=0;i<limit;i++)
	{
		if(i<r[i])
		{
			swap(a[i],a[r[i]]);
		}
	}
	for(long long mid=1;mid<limit;mid<<=1)
	{
		long long tmp=kasumi(g,(mod-1)/(mid*2));
		if(kd) tmp=kasumi(tmp,mod-2);
		for(long long i=0;i<limit;i+=mid*2)
		{
			long long w=1;
			for(long long j=0;j<mid;j++,w=w*tmp%mod)
			{
				long long x=a[i+j];
				long long y=w*a[i+j+mid]%mod;
				a[i+j]=(x+y+mod)%mod;
				a[i+j+mid]=(x-y+mod)%mod;
			}
			// cerr<<w<<endl;
		}
	}
	if(kd)
	{
		long long inv=kasumi(limit,mod-2);
		for(long long i=0;i<limit;i++)
		{
			a[i]=a[i]*inv%mod;
		}
	}
}

void mul(long long *a,long long *b)
{
	NTT(a,0);
	NTT(b,0);
	for(long long i=0;i<limit;i++)
	{
		a[i]=a[i]*b[i]%mod;
	}
	NTT(a,1);
	long long tmp=0;
	for(long long i=0;i<limit;i++)
	{
		n1[i]+=tmp;
		tmp=n1[i]/10;
		n1[i]%=10;
		if(n1[i]) len1=i+1;
	}
}

int main()
{
	long long ll=0;
	scanf("%s",s);
	long long len=strlen(s);
	while(limit<=400000) limit<<=1,ll++;
	for(int i=0;i<limit;i++)
	{
		r[i]=r[i>>1]>>1|((i&1)<<(ll-1));
	}
	reverse(s,s+len);
	for(int i=0;i<len;i++)
	{
		n1[i]=s[i]-'0';
	}
	long long x=1;
	for(int i=0;i<=len;i++)
	{
		n1[i]=n1[i]+x;
		x=n1[i]/10;
		n1[i]=n1[i]%10;
		if(n1[i]) len1=i+1;
	}
	x=1;
	for(int i=0;i<len1;i++)
	{
		n2[i]=n1[i]+x;
		x=n2[i]/10;
		n2[i]=n2[i]%10;
		if(n2[i]) len2=i+1;
	}
	x=1;
	for(int i=0;i<len2;i++)
	{
		n3[i]=n2[i]+x;
		x=n3[i]/10;
		n3[i]=n3[i]%10;
		if(n3[i]) len3=i+1;
	}
	x=1;
	for(int i=0;i<len3;i++)
	{
		n4[i]=n3[i]+x;
		x=n4[i]/10;
		n4[i]=n4[i]%10;
		if(n4[i]) len4=i+1;
	}
	mul(n1,n2);
	mul(n1,n3);
	mul(n1,n4);
	long long p=0,lenn=0;
	for(int i=len1-1;i>=0;i--)
	{
		p=p*10+n1[i];
		ans[i]=p/24;
		p%=24;
		if(ans[i]&&!lenn) lenn=i+1;
	}
	if(!lenn) lenn=1;
	for(int i=lenn-1;i>=0;i--)
	{
		printf("%lld",ans[i]);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_21829533/article/details/87868097