多项式练习记录

CF755G PolandBall and Many Other Balls

一排 \(n(n\le 10^9)\) 个球,一个组可以包含一个球或者两个相邻的球,一个球只能分一个组。求从这些球中取出 \(k(k<2^{15})\) 组的方案数.


枚举两个球的组个数 \(i\) ,有

\[\begin{aligned} f(k)&=\sum_{i=0}^k {n-i \choose k}{k\choose i}\\ \end{aligned} \]

然后就不会了 这时候 George1123 来指导了:直接推理比较困难,考虑转化组合意义:重复的最多 \(k\) 个,那么就相当于从前 \(k\) 个中选择 \(i\) 个,再从(除了这 \(i\) 个)剩下的球中选择 \(k\) 个。

其实思想就是把重复的都钦定在前 \(k\) 个里面。

注意到容斥重点在于前后选择不会重复。那么令 \(p(i)\) 表示恰好 \(i\) 个重复, \(q(i)\) 表示至少 \(i\) 个重复的方案数(这样一个至少一个恰好的形式似乎很常用),那么不难写出式子:

\[q(i)=\sum_{j=i}^k\binom{j}{i}p(j) \]

二项式反演一下,有

\[p(i)=\sum_{j=i}^k(-1)^{j-i}\binom{j}{i}q(j) \]

考虑怎么计算 \(q(j)\) ,先从前面 \(k\) 个钦定出 \(i\) 个重复的,再从 \(i\) 个之外选出 \(k-i\) 个,然后在 \(k-i\) 个中任意选或者不选(也就是至少 \(i\) 个中,钦定了 \(i\) 个,选之外的东西),于是有式子

\[q(i)=\binom{k}{i}\binom{n-i}{k-i}2^{k-i} \]

于是就能计算 \(f\)

\[\begin{aligned} f(k)=p(0)=\sum_{i=0}^k(-1)^iq(i)&=\sum_{i=0}^k(-1)^i\binom{k}{i}\binom{n-i}{k-i}2^{k-i}\\\\ &=\sum_{i=0}^k(-1)^i\dfrac{k!}{i!(k-i)!}\dfrac{(n-i)!}{(k-i)!(n-k)!}2^{k-i}\\\\ &=\dfrac{k!}{(n-k)!}\sum_{i=0}^k(-1)^i\dfrac{(n-i)!}{i!((k-i)!)^2}2^{k-i} \end{aligned} \]

看上去复杂度很靠谱,但是题目要求求所有的 \(f(i),i=1\sim k\) .

于是我又不会了 /kk . George1123 :化下降幂啊!

\[\begin{aligned} f(k)&=\frac{k!}{(n-k)!}\sum_{i=0}^k \frac{(-1)^i(n-i)!}{i!}\cdot \frac{2^{k-i}}{((k-i)!^2)}\\ &=\frac{k!n!}{(n-k)!}\sum_{i=0}^k \frac{(-1)^i(n-i)!}{i!n!}\cdot \frac{2^{k-i}}{((k-i)!^2)}\\ &=k!n^{\underline k}\sum_{i=0}^k \frac{(-1)^i}{i!n^{\underline i}}\cdot \frac{2^{k-i}}{((k-i)!^2)}\\ \end{aligned} \]

求和符号后面就是标准卷积式了。卷完之后再给每个 \(f[i]\) 乘上 \(i!n^{\underline i}\) 即可。

最优解 rk2 随便跑跑 /cy

Code
using namespace Poly;
int n,k,F[M],G[M],fac[M],ifac[M],dfac[M],idfac[M];

void Init( int m )
{
	fac[0]=1; for ( int i=1; i<=m; i++ ) fac[i]=1ll*fac[i-1]*i%Mod;
	ifac[m]=power(fac[m],Mod-2); for ( int i=m-1; i>=0; i-- ) ifac[i]=1ll*ifac[i+1]*(i+1)%Mod;
	dfac[0]=1; for ( int i=1; i<=m; i++ ) dfac[i]=1ll*dfac[i-1]*(n-i+1)%Mod;
	idfac[m]=power(dfac[m],Mod-2); for ( int i=m-1; i>=0; i-- ) idfac[i]=1ll*idfac[i+1]*(n-i)%Mod;
}

int main()
{
	n=read(); k=read(); int m=min(n,k); Init(m);
	k++;
	for ( int i=0; i<k; i++ )
	{
		F[i]=1ll*ifac[i]*idfac[i]%Mod; if ( i&1 ) F[i]=Mod-F[i];
		G[i]=power(2,i); G[i]=1ll*G[i]*ifac[i]%Mod*ifac[i]%Mod;
	}
	Poly_Mul( k,k,F,G,G );
	for ( int i=0; i<k; i++ ) G[i]=1ll*G[i]*fac[i]%Mod*dfac[i]%Mod;
	for ( int i=1; i<k; i++ ) printf( "%d ",G[i] );

	return 0;
}

P4921 [MtOI2018]情侣?给我烧了!

\(n\) 对CP随便坐两列座位,求恰好 \(k\) 对CP坐在一起的方案数。


看到恰好显然容斥。

\(F[n,k]\) 表示 \(n\) 对,恰好 \(k\) 对坐在一起的方案数,\(G[n,k]\) 表示 \(n\) 对,至少 \(k\) 对坐在一起的方案数。

那么有

\[G[n,k]=\binom{n}{k}k!2^k(2(n-k))!\\\\ G[n,k]=\sum_{i=k}^n\binom{i}{k}F[n,i] \]

直接上二项式反演

\[F[n,k]=\sum_{i=k}^n(-1)^{i-k}\binom ikG[n,i] \]

于是就可以直接求了w

\[\begin{aligned} ans&=\binom nk^2k!2^kF[n-k][0]\\\\ F[n][0]&=\sum_{i=0}^n(-1)^iG[n][i]\\\\ &=\sum_{i=0}^n(-1)^i\binom ni^2i!2^i(2(n-i))! \end{aligned} \]

Code
//Author: RingweEH
const int Mod=998244353,N=2010;
ll fac[N],ifac[N],pow2[N],F[N];
ll C( int n,int m ) { return fac[n]*ifac[m]%Mod*ifac[n-m]%Mod; }

void Init() {}		//省略预处理逆元和2的幂次
int main()
{
    Init();
    for ( int i=0; i<=1000; i++ )
    {
    //F[n,0]=\sum_{i=0}^n(-1)^i\binom ni^2i!2^i(2(n-i))!
        ll buf=1;
        for ( int j=0; j<=i; j++ )
        {
            ll nw=C(i,j);
            F[i]=(F[i]+nw*nw%Mod*buf%Mod*fac[j]%Mod*fac[(i-j)<<1])%Mod;
            buf=buf*(Mod-2)%Mod;
        }
    }

    int T=read();
    while ( T-- )
    {
        int n=read(); ll ans=0;
        for ( int i=0; i<=n; i++ )
        {
            ans=C(n,i);
            ans=ans*ans%Mod*fac[i]%Mod*pow2[i]%Mod*F[n-i]%Mod;
            printf( "%lld\n",ans );
        }
        //ans=\binom nk^2k!2^kF[n-k][0]
    }

    return 0;
}

UVA12298 Super Poker II

有一副扑克牌,对于每个合数 \(x\) ,均有四种花色面值为 \(x\) 的牌各一张,有些牌丢失了。对于 \(n=a\sim b\) ,求出:选出四张花色互不相同的牌且面值和为 \(n\) 的方案数。


对于四个花色(简写为 \(1\sim 4\) ),得到四个数列,\(A_1[i]\) 表示花色为 \(1\) ,面值为 \(i\) 的牌的数量。不难发现,\((A_1*A_2)[i]\) (加法卷积)就是取一张花色为 \(1\) 的牌和一张花色为 \(2\) 的牌,和为 \(i\) 的方案数(常见的背包+笛卡尔积思想),那么直接把 \(4\) 个式子卷起来就好了。不想写 FFT,于是去掏了一个大模数 /kel .

多测不清空,WAWA 两行泪……

Code
//Author: RingweEH
#define I128 __int128
const int N=2e5+10,M=N<<2,LIMIT=5e4+10;
int NW=5e4;
const ll Mod=39582418599937;
ll power( ll a,ll b ) { ll res=1; for (;b;b>>=1,a=(I128)a*a%Mod) if (b&1) res=(I128)a*res%Mod; return res; }
inline void bmod( ll &x ) { x-=Mod; x+=(x<0)*Mod; }
inline void pmod( ll &x ) { x=(x+Mod); x-=(x>Mod)*Mod; }
namespace Poly //预处理,NTT
int tot=0,pri[LIMIT];
bool vis[LIMIT];
void Sieve( int n=LIMIT-10 )   //省略线性筛
int a,b,c;
ll S[M],H[M],C[M],D[M],Arr[M],r1[M],r2[M];
char s[10];

void exread()
{
    int x=0; char ch=getchar();
    while ( ch>'9' || ch<'0' ) ch=getchar();
    while ( ch<='9' && ch>='0' ) x=x*10+ch-'0',ch=getchar();
    if ( ch=='S' ) S[x]--;
    else if ( ch=='H' ) H[x]--;
    else if ( ch=='C' ) C[x]--;
    else if ( ch=='D' ) D[x]--;
}

int main()
{
	Sieve(); 
	for ( int i=1; i<=LIMIT-10; i++ ) Arr[i]=(vis[i]) ? 1 : 0; Arr[1]=0;
	while ( 1 )
	{
		a=read(); b=read(); c=read(); NW=min(b+10,50000);
		if ( !a && !b && !c ) break;
		memcpy(S,Arr,sizeof(ll)*NW); memcpy(H,Arr,sizeof(ll)*NW);
		memcpy(C,Arr,sizeof(ll)*NW); memcpy(D,Arr,sizeof(ll)*NW);
		while ( c-- ) exread();
		Poly_Init(NW<<2);
		NTT(S,1); NTT(H,1); NTT(C,1); NTT(D,1);
		for ( int i=0; i<lim; i++ ) S[i]=(I128)S[i]*H[i]%Mod*C[i]%Mod*D[i]%Mod;
		reverse(S+1,S+lim); NTT(S,0);
		for ( int i=a; i<=b; i++ ) printf( "%lld\n",S[i] ); puts("");
		for ( int i=0; i<lim; i++ ) S[i]=H[i]=D[i]=C[i]=0;
	}

	return 0;
}

P4451 整数的lqp拆分

\[\sum\prod_{i=1}^m \text{Fib}_{a_i},m>0,a_1\sim a_m>0,\sum a_i=n \]

\(1e9+7\) 取模。


从这道题就能看出 OI 事业的进步,连我都能做 2011 国集答辩了。

根据所学的“ 骨牌问题 ”,不难想到这里可以看成是长度(值)为 \(n\) 的骨牌有 \(\tt Fib_n\) 种,而斐波那契数列的生成函数是 \(\dfrac{1}{1-x-x^2}\) ,所以不难得到这个问题的封闭形式是 \(\dfrac{1}{1-\frac{x}{1-x-x^2}}=\dfrac{1-x-x^2}{1-2x-x^2}\) .

我们仿照斐波那契数列的推导,不难得到分母的递推式是 \(a_n=2a_{n-1}+a_{n-2}\)

将分母写成递推式,有 \(a_n=2a_{n-1}+a_{n-2}\) ,那么 \(ans=a_n-a_{n-1}-a_{n-2}=a_{n-1}\) .

用特征方程求解得到 \(q=1\pm \sqrt 2\) ,带入 \(n=0,1\)\(F[n]=(1+\sqrt 2)^nx_0+(1-\sqrt 2)^nx_1\) (注意 \(ans=a_{n-1}\)

于是有 \(a_n=\dfrac{2-\sqrt 2}{4}(1-\sqrt 2)^n+\dfrac{2+\sqrt 2}{4}(1+\sqrt 2)^n\) ,而 \(n\leq 1e10000\) ,所以再用费马小定理化简一下即可。模意义下的 \(\sqrt 2\) 用二次剩余不难求出,是 59713600 ​和 940286407 .

Code
//Author: RingweEH
const int Mod=1e9+7,Invs2=59713600;
int bmod( int x ) { x-=Mod; x+=(x>>31)&Mod; return x; }
int n;
 
int main()
{
	n=Modread();
	int t1=bmod(1+Mod-Invs2),t2=bmod(1+Invs2);
	t1=power(t1,n-1); t2=power(t2,n-1);
	t1=1ll*t1*bmod(2+Mod-Invs2)%Mod;
	t2=1ll*t2*bmod(2+Invs2)%Mod;
	int ans=bmod(t1+t2);
	ans=1ll*ans*power(4,Mod-2)%Mod;
	printf( "%d\n",ans );

	return 0;
}

CF438E The Child and Binary Tree

称点权均在某个集合 \(C\) 中的有根二叉树为好的,求点权和为 \(s\) 的好的二叉树个数,对 \(998244353\) 取模。


\(G(x)=\sum x^{c_i},F(x)=\sum ans_ix^i\)

枚举一个节点的权值,再枚举左右儿子,对于 \(n>1\) ,有

\[F(n)=\sum_{i=1}^mG(i)\sum_{j=0}^{n-i}F(j)F(n-i-j) \]

所以 \(F(x)=1+G(x)*F^2(x)\) ,解得 \(F(x)=\dfrac{1\pm \sqrt{1-4G(x)}}{2G(x)}\) . 注意我们需要一个收敛的式子,所以由于 \(\lim\limits_{x\to 0}G(x)=0\) ,只能取负号。

最后有

\[F(x)=\dfrac{1- \sqrt{1-4G(x)}}{2G(x)}=\dfrac{2}{1+\sqrt{1-4G(x)}} \]

直接开方逆元即可。

Code
//Author: RingweEH
using namespace Poly;
int n,m,F[M],G[M],ans[M];

int main()
{
	n=read(); m=read()+1; Math::Init();
	F[0]=1;
	for ( int i=1,x; i<=n; i++ )
		if ( (x=read())<m ) F[x]=1;
	for ( int i=1; i<m; i++ ) bmod(F[i]=Mod-4ll*F[i]%Mod);
	Poly_Sqrt(m,F,G); bmod(++G[0]);
	Poly_Inv(m,G,ans);
	for ( int i=0; i<m; i++ ) bmod(ans[i]<<=1);
	for ( int i=1; i<m; i++ ) printf("%d\n",ans[i]);

	return 0;
}

CF917D Stranger Trees

给定一棵 \(n\) 点无权无向树,求对于这 \(n\) 个点以及 \(k=0\sim n-1\) ,有多少棵由这 \(n\) 个点之间的边构成的树,与给定树恰好有 \(k\) 条边重复,对 \(1e9+7\) 取模。


恰好 ——直接上二项式反演,问题转化为求至少 \(k\) 条边重合的方案数。那么就是选 \(k\) 条边,剩下 \(n-k\) 个连通块再任意连边。这就转化成了 Prufer序列小记 结尾的那个结论,方案数就是

\[n^{n-k-2}\prod_{i=1}^{n-k}siz[i] \]

我们可以给后面这个积找一个组合意义,也就是每个连通块内选一个点。那么一个树形DP就来了:设 \(f[i][j][k]\) 表示以 \(i\) 为根的子树,选了 \(j\) 个连通块,\(i\) 所在的连通块是否选点( \(k\) )的方案数,至少 \(k\) 条边重合的方案数就是 \(f[1][n-k][1]\) ,再反演回去即可。复杂度 \(\mathcal{O}(n^2)\)

好像正解是矩阵树定理?不管了 反正我不会

Code
//Author: RingweEH
#define pb push_back
const int N=110,Mod=1e9+7;
int power( int a,int b ) { int res=1; for ( ; b; b>>=1,a=1ll*a*a%Mod ) if ( b&1 ) res=1ll*res*a%Mod; return res; }
int n,F[N],G[N],C[N][N],f[N][N][2],g[N][2],siz[N];
vector<int> Ed[N];

void Init()		//预处理组合数
void Add( int &x,int v ) { x=(x+v-Mod); x+=(x>>31)&Mod; }
void DFS( int u,int fa )
{
    int (*nw)[2]=f[u]; nw[1][0]=nw[1][1]=1;
    siz[u]=1;
    for ( int t=0; t<Ed[u].size(); t++ )
    {
        int v=Ed[u][t];
        if ( v==fa ) continue;
        DFS(v,u); int (*las)[2]=f[v];
        for ( int i=0; i<=siz[u]; i++ )
            for ( int j=0; j<=siz[v]; j++ )
            {
                Add( g[i+j][0],1ll*nw[i][0]*las[j][1]%Mod );
                Add( g[i+j][1],1ll*nw[i][1]*las[j][1]%Mod );
                if ( i+j )
                {
                    Add( g[i+j-1][0],1ll*nw[i][0]*las[j][0]%Mod );
                    Add( g[i+j-1][1],1ll*nw[i][1]*las[j][0]%Mod );
                    Add( g[i+j-1][1],1ll*nw[i][0]*las[j][1]%Mod );
                }
            }
        for ( int i=0; i<=siz[u]+siz[v]; i++ )
            f[u][i][0]=g[i][0],f[u][i][1]=g[i][1],g[i][0]=g[i][1]=0;
        siz[u]+=siz[v];
    }
}

int main()
{
    n=read(); 
    for ( int i=1,u,v; i<n; i++ )   
        u=read(),v=read(),Ed[u].pb(v),Ed[v].pb(u);

    Init(); DFS(1,0);
    for ( int i=0; i<n-1; i++ ) G[i]=1ll*f[1][n-i][1]*power(n,n-i-2)%Mod;
    G[n-1]=1;
    for ( int i=0; i<n; i++ )
        for ( int j=i; j<n; j++ )
            if ( (j-i)&1 ) Add(F[i],Mod-1ll*G[j]*C[j][i]%Mod);
            else Add( F[i],1ll*G[j]*C[j][i]%Mod );

    for ( int i=0; i<n; i++ ) printf( "%d ",F[i] ); puts("");

    return 0;
}

HDU1521 排列组合

给定 \(n\) 种物品和数量,求选出 \(m\) 件的排列数。


仿照染色问题,对于有 \(num[i]\) 个的第 \(i\) 种物品,不难得到其 EGF\(\sum\limits_{j=0}^{num[i]}\dfrac{x^j}{j!}\) ,各个颜色的 EGF 卷起来即可。\(n,m\) 只有 \(10\) ,暴力卷积就好了。

Code
//Author: RingweEH
memset( F,0,sizeof(F) ); memset( G,0,sizeof(G) );
for ( int i=1; i<=n; i++ ) num[i]=read();
for ( int i=0; i<=num[1]; i++ )
	F[i]=1.0/fac[i];
for ( int i=2; i<=n; i++ )
{
	for ( int j=0; j<=m; j++ )
		for ( int k=0; k<=num[i] && j+k<=m; k++ )
			G[j+k]+=F[j]/fac[k];
	for ( int j=0; j<=m; j++ )
		F[j]=G[j],G[j]=0;
}
printf( "%.0lf\n",F[m]*fac[m] );

P5219 无聊的水题 I

\(n\) 点、最大度数为 \(m\) 的树的个数。


最大度数为 \(m\) 就是 “恰好 \(m\) ” ,显然可以把它容斥掉。而关于度数的计树题很容易想到 Prufer 序列,度数不超过 \(m\) 就是在序列中每个点出现次数不超过 \(m-1\) 。现在问题转化为:一个长度为 \(n-2\) 的序列,每个元素的取值为 \([1,n]\) ,每个值出现次数不超过 \(m\) ,求方案数。

\([1,n]\) 来一个 EGF ,正好是 \(\sum_{i=0}^m\dfrac{x^i}{i!}\) ,并且全部一样,那就直接快速幂就好了。

最优解 rk1/cy .

Code
//Author: RingweEH
using Poly::Poly_Power;

int n,m,F[M],G[M];

int Get( int n,int m )
{
	memset( F,0,sizeof(F) );
	for ( int i=0; i<=m; i++ ) F[i]=Math::infac[i];
	Clear(G,n); Poly_Power(n,n,F,G);
	return 1ll*G[n-2]*Math::fac[n-2]%Mod;
}

int main()
{
	Math::Init();
	n=read(); m=read();
	printf( "%d\n",(Get(n,m-1)-Get(n,m-2)+Mod)%Mod );

	return 0;
}

P5339 唱、跳、rap和篮球

求合法排列数使得不会有恰好 \(1\sim 4\) 类的人排在一起。


设恰好有 \(1\sim 4\)\(k\sim k+3\) 的情况叫 \(A\) ,那么题意就是恰好 \(0\)\(A\) ,设 \(F[i]\) 表示至少有 \(i\)\(A\)\(G[i]\) 表示恰好 \(i\) 个,上二项式反演。现在就是求出至少的个数。

\[\displaystyle F[i]=\binom{n-3i}{i}\times calc(i) \]

\(calc\)\(a-i,b-i,c-i,d-i\) 个人排成长度为 \(n-4i\) 的方案数,这个东西直接把 4EGF 卷一卷就好了。于是这题就莫得了!

Code
//Author: RingweEH
using Math :: power;
using Math :: bmod;
using Math :: C;
using namespace Poly;

int Calc( int pos,int a,int b,int c,int d )
{
	static int A[M],B[M],C[M],D[M]; Poly_Init(a+b+c+d);
	for ( int i=0; i<a; i++ ) A[i]=Math::infac[i]; Clear(A+a,lim-a);
	for ( int i=0; i<b; i++ ) B[i]=Math::infac[i]; Clear(B+b,lim-b);
	for ( int i=0; i<c; i++ ) C[i]=Math::infac[i]; Clear(C+c,lim-c);
	for ( int i=0; i<d; i++ ) D[i]=Math::infac[i]; Clear(D+d,lim-d);
	NTT(A,1); NTT(B,1); NTT(C,1); NTT(D,1);
	for ( int i=0; i<lim; i++ ) A[i]=1ll*A[i]*B[i]%Mod*C[i]%Mod*D[i]%Mod;
	reverse(A+1,A+lim); NTT(A,0); return A[pos];
}

int n,a,b,c,d,F[510],G[510];

int main()
{
	Math::Init();
	n=read(); a=read(); b=read(); c=read(); d=read();

	int lim=min(n>>2,min(a,min(b,min(c,d))));
	for ( int i=0; i<=lim; i++ ) G[i]=Calc(n-4*i,a-i+1,b-i+1,c-i+1,d-i+1);
	for ( int i=0; i<=lim; i++ ) F[i]=1ll*C(n-3*i,i)*G[i]%Mod*Math::fac[n-4*i]%Mod;
	int ans=0;
	for ( int i=0; i<=lim; i++ ) bmod(ans+=(i&1)?(Mod-F[i]):F[i]);

	printf( "%d\n",ans );

	return 0;
}

P4841 城市规划

\(n\) 点简单有标号无向连通图数。


数树完了来数图!注意到 “连通图” 不仅是一个限制更严格的图,同时也是一个简单有标号无向图(以下简称为一般图)的基本组成单位。那么如果连通图的 EGF\(F(x)\) ,一般图的 EGF 就不难得到,也就是 \(\exp F(x)\) ,设为 \(G(x)\) ,那么 \(F(x)=\ln G(x)\)

问题转化为了求 \(G(x)\) 。一般图总共 \(\binom{n}{2}\) 条边,方案数就是 \(\displaystyle2^{\binom{n}{2}}\) ,直接上多项式 \(\ln\) 即可。

Code
//Author: RingweEH
using Poly:: Poly_ln;
int n,F[M];

int main()
{
	n=read(); Math::Init();
	for ( int i=0; i<n+1; i++ ) F[i]=power(2,(1ll*i*(i-1)/2)%(Mod-1)),F[i]=1ll*F[i]*Math::infac[i]%Mod;
	Poly_ln( n+1,F,F );
	printf( "%lld\n",1ll*F[n]*Math::fac[n]%Mod );

	return 0;
}

P5162 WD与积木

\(n\) 个高为 \(1\) 的正方形积木,随机大小并标号。相同大小同一层,所有层从大到小对方,求不同堆法的层数期望。


对于每一层的积木,EGF\(F(x)=\sum\limits_{i=1}\dfrac{x^i}{i!}=e^x-1\) ,那么一个 \(i\) 层的东西就是 \(F^i(x)\) ,总方案就直接求和:\(G(x)=\sum\limits_{i=0}F^i(x)=\dfrac{1}{1-F(x)}=\dfrac{1}{2-e^x}\) .

现在要算总贡献,那就是再乘个层数,然后随便差分:

\[H(x)=\sum_{i=1}iF^i(x)=\dfrac{\sum_{i=1}F^i(x)}{1-F(x)}=\dfrac{F(x)}{(1-F(x))^2} \]

算期望就直接总贡献除以方案数即可。

Code
//Author: RingweEH
using namespace Poly;
int n,F[M],G[M],tf[M],tg[M];

int main()
{
	int n=100000;
	Math::Init();
	tf[0]=1;
	for ( int i=1; i<=n; i++ ) tf[i]=Mod-infac[i];
	for ( int i=1; i<=n; i++ ) G[i]=infac[i];
	Poly_Inv(n+1,tf,F); Poly_Mul(n+1,n+1,tf,tf,tf );
	Poly_Inv(n+1,tf,tg); Poly_Mul(n+1,n+1,G,tg,G);
	int T=read();
	while ( T-- )
	{
		n=read();
		printf("%lld\n",1ll*G[n]*power(F[n],Mod-2)%Mod );
	}

	return 0;
}

P4548 歌唱王国

随机生成一个取值在 \([1,n]\) 的整数序列,给出 \(t\) 个序列,生成在出现一个钦定的序列后停止。问出现每个给定序列然后停止的序列长度期望。


一点不会,找了半天 PGF 的博客都说只能讲题……所以就来看题解了。

\(F[n]\) 为恰好在第 \(n\) 次结束的概率,\(G[n]\) 为到了第 \(n\) 次仍未结束的概率,答案就是 \(E(X)=F'(1)\) 。设 \(F,G\) 为二者的 PGF ,那么 \(F'(1)\) 即为所求。由于一个数要么结束,要么没结束,所以有式子:

\[F(z)+G(z)=zG(z)+1 \]

求导得到

\[F'(x)+G'(x)=xG'(x)+G(x)=>F'(x)=(x-1)G'(x)+G(x) \]

\(x=1\) 时,有 \(F'(1)=G(1)\) .

注意到,在一个没有结束的式子后面加上名字一定会结束,但是有可能在没有到整个名字就结束了(此时添加的序列是名字的一个 border),记字符集大小为 \(n\) ,名字长度为 \(m\) ,令 \(a[i]\) 表示 \([1,i]\) 是否是名字的一个 border ,那么可以列出式子:

\[G(x)\cdot \left(\dfrac 1n x\right)^m=\sum_{i=1}^ma[i]\cdot F(x)\cdot\left(\dfrac 1n x\right)^{m-i} \]

\(x=1\) 时,有 \(G(1)=\sum\limits_{i=1}^ma[i]\cdot F(1)\cdot n^i=\sum\limits_{i=1}^ma[i]\cdot n^i\)

于是 \(F'(1)=\sum\limits_{i=1}^ma[i]n^i\) ,KMP 求出 \(a[i]\) 然后直接算即可。

一个 getchar()fread 优化能松到 rk1 ……

Code
//Author: RingweEH
const int N=1e5+10,Mod=10000;
int n,m,t,s[N],powe[N],nxt[N];

inline void bmod( int &x ) { x-=Mod; x+=(x>>31)&Mod; }

void Work()
{
	m=read();
	for ( int i=1; i<=m; i++ ) s[i]=read();
	for ( int i=2,j=0; i<=m; i++ )
	{
		while ( j && s[j+1]!=s[i] ) j=nxt[j];
		if ( s[j+1]==s[i] ) j++;
		nxt[i]=j;
	}
	int ans=0;
	for ( int i=m; i; i=nxt[i] ) bmod(ans+=powe[i]);
	printf("%04d\n",ans );
}

int main()
{
	n=read(); t=read();
	powe[0]=1;
	for ( int i=1; i<=N-10; i++ ) powe[i]=1ll*powe[i-1]*n%Mod;
	while ( t-- ) Work();

	return 0;
}

P3706 硬币游戏

给定 \(n\) 个长度为 \(m\) 的硬币序列,出现其中一个就停止,问等概率情况下每个人的胜率。


上一题的加强版,匹配串是多个而不是一个。但是注意到 \(n\) 并不大,所以可以直接开 \(n\) 个。

和上题一样,设 \([x^n]G(x)\) 表示 \(n\) 轮之后仍然没有结束的概率,\([x^n]F_k(x)\) 表示第 \(k\) 个人在第 \(n\) 轮获胜的概率。

同样地,仍然可以列出两个式子:

\[xG(x)+1=G(x)+\sum F_k(x) \]

\(a[i][j][k]\) 表示第 \(i\) 个串的 \([1,\cdots ,k]\) 是否和第 \(j\) 个串的 \([m-k+1,m]\) 匹配。这个可以 Hash 直接求,反正数据不大。然后同样在后面接一个串,这里的 \(i\) 取值为 \([1,n]\)

\[G(x)\cdot\left(\dfrac x2\right)^m=\sum_{j=1}^n\sum_{k=1}^ma[i][j][k]\cdot F_j(x)\cdot\left(\dfrac x2\right)^{m-k} \]

还是老办法,令 \(x=1\) 得到:

\[\sum F_k(x)=1\\\\ G(1)=\sum_{j=1}^na[i][j][k]\cdot F_j(1)\cdot 2^k \]

于是直接高斯消元即可。为什么这个存的下啊/jk

Code
//Author: RingweEH
const int N=310,base1=31,base2=37,Mod1=998244353,Mod2=1e9+7;
int n,m,has1[N][N],has2[N][N],powe1[N],powe2[N];
db a[N][N],powe[N],ans[N];
char s[N];

inline int bmod( int x,int Mod ) { x-=Mod; x+=(x>>31)&Mod; return x; }

int Geth1( int num,int l,int r )
{
	return bmod(has1[num][r]-1ll*has1[num][l-1]*powe1[r-l+1]%Mod1+Mod1,Mod1);
}

int Geth2( int num,int l,int r )
{
	return bmod(has2[num][r]-1ll*has2[num][l-1]*powe2[r-l+1]%Mod2+Mod2,Mod2);
}

void Gauss( int n )
{
	for ( int i=1; i<=n; i++ )
	{
		int p=i;
		for ( int j=i; j<=n; j++ )
			if ( fabs(a[j][i])>fabs(a[p][i]) ) p=j;
		if ( p^i )
		{
			for ( int j=1; j<=n+1; j++ ) swap( a[p][j],a[i][j] );
		}
		for ( int j=1; j<=n; j++ )
		{
			if ( i==j ) continue;
			db tmp=a[j][i]/a[i][i];
			for ( int k=i; k<=n+1; k++ ) a[j][k]-=a[i][k]*tmp;
		}
	}
	for ( int i=1; i<=n; i++ ) a[i][n+1]/=a[i][i];
}

void Hash_Init()
{
	powe[0]=powe1[0]=powe2[0]=1;
	for ( int i=1; i<=m; i++ )
	{
		powe[i]=powe[i-1]*2.0;
		powe1[i]=1ll*powe1[i-1]*base1%Mod1;
		powe2[i]=1ll*powe2[i-1]*base2%Mod2;
	}
	for ( int i=1; i<=n; i++ )
	{
		scanf( "%s",s+1 ); has1[i][0]=has2[i][0]=0;
		for ( int j=1; j<=m; j++ )
		{
			int c=(s[j]=='H')+1;
			has1[i][j]=(1ll*has1[i][j-1]*base1%Mod1+c)%Mod1;
			has2[i][j]=(1ll*has2[i][j-1]*base2%Mod2+c)%Mod2;
		}
	}
}

int main()
{
	scanf( "%d%d",&n,&m); 
	Hash_Init();
	for ( int i=1; i<=n; i++ )
	{
		for ( int j=1; j<=n; j++ )
			for ( int k=1; k<=m; k++ )
				if ( Geth1(i,1,k)==Geth1(j,m-k+1,m) && Geth2(i,1,k)==Geth2(j,m-k+1,m) )
					a[i][j]+=powe[k];
		a[i][n+1]=-1; a[i][n+2]=0; a[n+1][i]=1;
	}

	a[n+1][n+1]=0; a[n+1][n+2]=1;
	Gauss(n+1);
	for ( int i=1; i<=n; i++ ) printf( "%.10lf\n",a[i][n+2] );

	return 0;
}

HDU4652 Dice

\(m\) 面的骰子和两种询问,问出现 \(n\) 个相同结果的期望和出现 \(n\) 个不同结果的期望。


相对于上题增加了字符集,改为两种匹配串。

我总觉得这题用 PGF 有亿点麻烦\(F[i]\) 为第 \(i\) 步结束的概率,\(G[i]\) 反之。无论是第一问还是第二问,每个串都是等价的,所以可以先统一考虑,设终止串的总数有 \(cnt\) 个,那么在某个串结束的 PGF 就是 \(\dfrac{F(x)}{cnt}\)

同样,设 \(a[i]\) 表示长为 \(i\) 的后缀等于加入串 \(S[1,i]\) 的个数。不难列出式子:

\[G(x)=\sum\limits_{i=1}^na[i]\dfrac{F(x)}{cnt}m^i \]

\(x=1\) 代入得:

\[G(1)=\sum_{i=1}^na[i]\dfrac{m^i}{cnt} \]

对于第一问,\(cnt=m,a[i]=1\) ;对于第二问,\(cnt=m^{\underline{n}},a[i]=(m-i)^{\underline{n-i}}\) ,代入求值即可。

posted @ 2021-01-28 10:53  MontesquieuE  阅读(123)  评论(0编辑  收藏  举报