[ZJOI2019]开关

一、题目

点此看题

二、解法

考虑每个灯的生成函数,那么答案的生成函数为它们的乘积,设 P = p i P=\sum p_i (这里用 e e 不是特别严谨,因为本题不需要指数型生成函数,但是为了方便表示无穷级数,使用 e e 可能会更加简洁):
F ( x ) = i = 1 n e p i P x + ( 1 ) s i e p i P x 2 F(x)=\prod_{i=1}^n \frac{e^{\frac{p_i}{P}x}+(-1)^{s_i}e^{-\frac{p_i}{P}x}}{2} 但是还有一个问题,就是上面的生成函数并不会在第一次满足条件是就停止,我们考虑转满一圈,也就是操作会原状态的生成函数:
G ( x ) = i = 1 n e p i P x + e p i P x 2 G(x)=\prod_{i=1}^n \frac{e^{\frac{p_i}{P}x}+e^{-\frac{p_i}{P}x}}{2} 求出 F , G F,G 直接暴力背包,时间复杂度 O ( n p ) O(np) ,考虑答案的生成函数为 H ( x ) H(x) ,则满足 h ( x ) g ( x ) = f ( x ) h(x)g(x)=f(x) h , g , f h,g,f H , G , F H,G,F 的普通生成函数),考虑使用闭形式来表示它们之间的转化,它们之间满足这样的转化关系:
f ( x ) = i = P P a i 1 i P x f(x)=\sum_{i=-P}^{P} \frac{a_i}{1-\frac{i}{P}x} 此时要求的答案是 h ( 1 ) h'(1) ,求导就是把每种概率乘上他的权值(步数),算出来就是期望,可以使用这个公式,( 1 g ( x ) \frac{1}{g(x)} 的求导可以当做复合函数求导,也就是加入一个辅助函数 f ( x ) = 1 x f(x)=\frac{1}{x} ):
( f ( x ) g ( x ) ) = ( f ( x ) g ( x ) 1 ) = f ( x ) g ( x ) f ( x ) g ( x ) g ( x ) 2 (\frac{f(x)}{g(x)})'=(f(x)g(x)^{-1})'=\frac{f'(x)g(x)-f(x)g'(x)}{g(x)^2} 发现这样算出来的 h h 是不收敛的,我们把 f , g f,g 同乘 1 i p \prod1-\frac{i}p{} ,现在的问题在于解决 f ( x ) f(x) 的导数,我们来推式子:
f ( x ) = a i j i 1 j p x f(x)=\sum a_i\prod_{j\not=i}1-\frac{j}{p}x 为了方便表示,我们设 g i ( x ) = a i j i j p x , h i ( x ) = 1 i p x g_i(x)=a_i\prod_{j\not=i}\frac{j}{p}x,h_i(x)=1-\frac{i}p{x} ,我们想对 g g 求导:
g i ( x ) = a i j i h j ( x ) g_i'(x)=a_i\prod_{j\not=i}h_j(x)' g i ( x ) = a i k i ( j i , j k h j ( x ) ) × h k ( x ) g_i'(x)=a_i\sum_{k\not=i}(\prod_{j\not=i,j\not=k}h_j(x))\times h_k(x)' g i ( x ) = a i k i ( j i h j ( x ) ) × h k ( x ) h k ( x ) . . . . . . g_i'(x)=a_i\sum_{k\not=i}(\prod_{j\not=i}h_j(x))\times \frac{h_k(x)'}{h_k(x)}......* g i ( x ) = a i ( j i h j ( x ) ) k i h k ( x ) h k ( x ) g_i'(x)=a_i(\prod_{j\not=i}h_j(x))\sum_{k\not=i}\frac{h_k(x)'}{h_k(x)} g i ( x ) = a i ( j i 1 j p x ) k i k p 1 k p x g_i'(x)=a_i(\prod_{j\not=i}1-\frac{j}{p}x)\sum_{k\not=i}\frac{-\frac{k}{p}}{1-\frac{k}{p}x} 那么 f ( x ) f'(x) 就算出来了,长成这样:
f ( x ) = i a i ( j i 1 j p x ) k i k p 1 k p x f'(x)=\sum_ia_i(\prod_{j\not=i}1-\frac{j}{p}x)\sum_{k\not=i}\frac{-\frac{k}{p}}{1-\frac{k}{p}x} 考虑求除 f ( 1 ) f'(1) 的值,可以分类讨论,由于 x = 1 x=1 是, 1 i p 1-\frac{i}{p} 很特殊,我们考虑 i i 是否等于 p p

  • i p i\not=p ,考虑到 j j 的枚举中会有 0 0 这一项产生( j = p j=p 时),那就都消完了?等等,看我标注了*号的柿子,我们为了提出相同的一项而乘上又除去了 1 k p x 1-\frac{k}p{x} ,所以在 k k 中产生的 1 0 \frac{-1}{0} 必须要与 0 0 的一项对消,产生 1 -1 ,所以当 i p i\not=p 时, k k 只能等于 p p ,这样我们就可以对 g g 函数进行化简:
    g i ( x ) = a i j i , j p 1 j p x g_i'(x)=-a_i\prod_{j\not=i,j\not=p}1-\frac{j}p{x} g i ( x ) = j p 1 j p x × a i 1 i p x g_i'(x)=-\prod_{j\not=p}1-\frac{j}{p}x\times\frac{a_i}{1-\frac{i}{p}x}
  • i = p i=p ,直接带推出的 f ( x ) f'(x) 的柿子去算,比较简单。

分类讨论之后,我们可以知道 f ( 1 ) f'(1) 的值:
f ( 1 ) = ( i p 1 i p ) ( i p a i 1 i p + a p i p i p 1 i p ) f'(1)=-(\prod_{i\not=p}1-\frac{i}{p})(\sum_{i\not=p}\frac{a_i}{1-\frac{i}{p}}+a_p\sum_{i\not=p}\frac{\frac{i}{p}}{1-\frac{i}{p}}) 知道了 f ( 1 ) f'(1) 的求导之后, g ( 1 ) g'(1) 也很容易(好像和推导的重名了,写到这里才发现,请谅解),我们可以带入上面对分式求导的柿子,就可以算出答案(设 a i a_i f f 的系数, b i b_i g g 的系数,容易发现 a p = b p = 1 2 n a_p=b_p=\frac{1}{2^n} ):
2 n i p b i a i 1 i p 2^n\sum_{i\not=p}\frac{b_i-a_i}{1-\frac{i}{p}} 写的我要吐了,贴上简洁的代码:

#include <cstdio>
#include <cstring>
#define int long long
const int M = 50005;
const int MOD = 998244353;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,sum,inv,ans,s[105],tmp[2*M],f[2*M],g[2*M];
int qkpow(int a,int b)
{
    int r=1;
    while(b>0)
    {
        if(b&1) r=r*a%MOD;
        a=a*a%MOD;
        b>>=1;
    }
    return r;
}
signed main()
{
    n=read();
    for(int i=1; i<=n; i++)
        s[i]=read();
    inv=qkpow(2,MOD-2);
    f[M]=g[M]=1;
    for(int i=1; i<=n; i++)
    {
        int x=read();
        sum+=x;
        memset(tmp,0,sizeof tmp);
        for(int j=-sum; j+x<=sum; j++) tmp[j+x+M]=(tmp[j+x+M]+inv*f[j+M])%MOD;
        for(int j=-sum+x; j<=sum; j++) tmp[j-x+M]=(tmp[j-x+M]+(s[i]?MOD-inv:inv)*f[j+M])%MOD;
        memcpy(f,tmp,sizeof tmp);
        //
        memset(tmp,0,sizeof tmp);
        for(int j=-sum; j+x<=sum; j++) tmp[j+x+M]=(tmp[j+x+M]+inv*g[j+M])%MOD;
        for(int j=-sum+x; j<=sum; j++) tmp[j-x+M]=(tmp[j-x+M]+inv*g[j+M])%MOD;
        memcpy(g,tmp,sizeof tmp);
    }
    for(int i=-sum; i<=sum; i++) ans=(ans+(g[i+M]-f[i+M]+MOD)*qkpow(sum-i,MOD-2))%MOD;
    printf("%lld\n",ans*sum%MOD*qkpow(2,n)%MOD);
}
发布了192 篇原创文章 · 获赞 12 · 访问量 3350

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/103806397