UOJ449 集训队作业2018 喂鸽子

Problem

UOJ

看题后:

  • boshi:这是一道简单题
  • 队长:这题好像不难,感觉和猎人杀有点像
  • 我:
    UOJ449 集训队作业2018 喂鸽子_第1张图片

Solution

感觉自己越来越菜了,再这样下去,要是正式考试送温暖岂不是连温暖都拿不到了。。

一脸min-max反演的样子,由于每个鸽子都等价,枚举子集大小 i i i 即可

a n s = ∑ i = 1 n ( n i ) ( − 1 ) i + 1 n i f ( i ) ans=\sum_{i=1}^n\binom n i(-1)^{i+1}\frac n i f(i) ans=i=1n(in)(1)i+1inf(i)

其中 n i \frac n i in 来源于平均每 n i \frac n i in 步才会有一粒喂给选中的鸽子。 f ( i ) f(i) f(i) 表示的是给 i i i 只鸽子喂食,有一个鸽子大于等于 k k k 时停止的期望步数。

枚举喂给其他鸽子的玉米粒数量,概率通过方案数来算

f ( i ) = ∑ j = 0 ( i − 1 ) ( k − 1 ) ( j + k ) i ( j + k − 1 j ) g [ i − 1 ] [ j ] ( 1 i ) j + k f(i)=\sum_{j=0}^{(i-1)(k-1)} (j+k)i\binom {j+k-1} jg[i-1][j] (\frac 1 i)^{j+k} f(i)=j=0(i1)(k1)(j+k)i(jj+k1)g[i1][j](i1)j+k

其中 g [ i − 1 ] [ j ] g[i-1][j] g[i1][j] 表示给 i − 1 i-1 i1 只鸽子喂 j j j 粒,且每只都小于 k k k 的方案数。这个可以用生成函数算

g [ i − 1 ] [ j ] = ( ∑ r = 0 k − 1 x i i ! ) i − 1 [ j ] g[i-1][j]=(\sum_{r=0}^{k-1} \frac {x^i} {i!})^{i-1}[j] g[i1][j]=(r=0k1i!xi)i1[j]

时间复杂度 O ( n 2 k log ⁡ ( n k ) ) O(n^2k\log (nk)) O(n2klog(nk))

Code

#include 
#include 
using namespace std;
typedef long long ll;
const int maxn=70010,mod=998244353,G=3;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
int n,k,N,l,ans,fac[maxn],inv[maxn],f[55],g[55][maxn],rev[maxn];
int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x<y?x-y+mod:x-y;}
int C(int n,int m){return m>n?0:(ll)fac[n]*inv[m]%mod*inv[n-m]%mod;}
int power(int x,int y)
{
	int res=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1)
	    res=(ll)res*x%mod;
	return res;
}
void NTT(int *a,int f)
{
	for(int i=1;i<N;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1;i<N;i<<=1)
	{
		int gn=power(G,(mod-1)/(i<<1));
		for(int j=0;j<N;j+=(i<<1))
		{
			int g=1,x,y;
			for(int k=0;k<i;++k,g=(ll)g*gn%mod)
			{
				x=a[j+k];y=(ll)g*a[j+k+i]%mod;
				a[j+k]=pls(x,y);a[j+k+i]=dec(x,y);
			}
		}
	}
	if(f==-1)
	{
		int iv=power(N,mod-2);reverse(a+1,a+N);
		for(int i=0;i<N;i++) a[i]=(ll)a[i]*iv%mod;
	}
}
void init(int N)
{
	fac[0]=1;
	for(int i=1;i<=N;i++) fac[i]=(ll)fac[i-1]*i%mod;
	inv[N]=power(fac[N],mod-2);
	for(int i=N-1;~i;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod;
}
int main()
{
	read(n);read(k);
	init(n*k);
	for(N=1,l=0;N<=(n*k);N<<=1) ++l;
	for(int i=1;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<k;i++) g[1][i]=inv[i];
	NTT(g[1],1);g[0][0]=1;
	for(int i=2;i<n;i++)
	  for(int j=0;j<N;j++)
	    g[i][j]=(ll)g[i-1][j]*g[1][j]%mod;
	for(int i=1;i<=1||i<n;i++) NTT(g[i],-1);
	for(int i=1;i<=n;i++)
	{
		int inv=power(i,mod-2);int tmp=power(inv,k-1);
		for(int j=0;j<N;j++,tmp=(ll)tmp*inv%mod)
		  f[i]=(f[i]+(ll)(j+k)*C(j+k-1,j)%mod*g[i-1][j]%mod*fac[j]%mod*tmp)%mod;
	}
	for(int i=1;i<=n;i++)
	{
		if(i&1) ans=(ans+(ll)C(n,i)*n%mod*power(i,mod-2)%mod*f[i])%mod;
		else ans=dec(ans,(ll)C(n,i)*n%mod*power(i,mod-2)%mod*f[i]%mod)%mod;
	}
	printf("%d\n",ans);
	return 0;
}

你可能感兴趣的:(反演及容斥,多项式,生成函数,UOJ)