CodeForces 1408I Bitwise Magic

题意

给定三个整数 \(n,k,c\) 和一个长度为 \(n\) 的序列 \(a\),保证 \(a_i\) 互不相同。可以操作 \(k\) 次,每次随机选择一个 \(a_i\) 变成 \(a_i-1\)。问最后的序列异或和为 \(0,\cdots 2^{c}-1\) 的概率。

\(\texttt{Data Range:}k,c\leq 16,k\leq a_i\leq 2^c-1,n\leq 2^{c}-k\)

题解

先来看一个显而易见的结论:对于 \(x\leq 2^c-1\),本质不同的 \(k\) 元组 \((x\oplus(x-1),x\oplus(x-2),\cdots,x\oplus(x-k))\) 的个数大约是 \(O(ck)\) 的。

首先注意到 \(x\oplus(x-1)\) 这个东西的取值只能是 \(2^t-1\) 的形式,其中 \(t=\log\operatorname{lowbit}(x)+1\)。考虑分类讨论。

  • 如果 \(t>\log k\),因为 \(x\oplus (x-k)=(x\oplus (x-1))\oplus((x-1)\oplus(x-2))\cdots\),那么比 \(t\) 高的位不受影响,比 \(t\) 低的位因为是从 \(2^t-1\) 开始一个一个减的,与 \(x\) 无关,所有只有 \(O(c)\) 种取值。

  • 如果 \(t\leq\log k\),那么这东西的取值只与 \(\log k\) 位往上走第一个 \(1\) 的位置和这 \(\log k\) 位的取值相关,所以取值为 \(O(c2^{\log k})=O(ck)\) 的。

经暴力计算可得 \(c=k=16\) 的时候只有 \(192\) 种本质不同的 \(k\) 元组。

由于直接数不好数,这里我们考虑每一次操作怎么改变答案的。容易看出如果对 \(a_i\) 操作 \(j\) 次的话会给原来的答案异或上 \(a_i\oplus (a_i-j)\)

这个时候可以数一下本质不同操作序列的个数。由于操作序列随机排列是本质相同的,所以需要使用 EGF。为了方便,这里设 \(d_{i,j}=a_i\oplus(a_i-j)\),那么位置 \(i\) 的 EGF 为

\[\sum\limits_{j=0}^{k}\frac{x^{d_{i,j}}y^j}{j!} \]

这里 \(x\) 枚举的是这个位置的值,\(y\) 枚举的是操作次数。将每个位置的这些东西乘起来得到总共的 EGF:

\[F(x,y)=\prod_{i=1}^{n}\left(\sum\limits_{j=0}^{k}\frac{x^{d_{i,j}}y^j}{j!}\right) \]

其中 \(x\) 这个维度为异或卷积,\(y\) 为加法卷积。那么 \(v![x^u][y^v]\) 就表示操作了 \(v\) 次之后使得答案为 \(u\oplus a_1\oplus\cdots\oplus a_n\) 的方案数。注意到操作顺序不影响操作结果,在 EGF 的过程中去掉了这个影响,在最后统计的时候要乘回来。

考虑如何简化计算。对于每一个本质不同的 \(k\) 元组 \((x\oplus(x-1),x\oplus(x-2),\cdots,x\oplus(x-k))\) 来说,它对应的 EGF 是 \(x\) 操作若干次之后对答案的影响。于是我们可以设 \(G_i(x,y)\) 为第 \(i\) 中本质不同的 \(k\) 元组对应的 EGF,\(r_i\) 为出现次数,于是也可以这么表示 \(F\)

\[F(x,y)=\prod_{i}G^{r_i}_i(x,y) \]

这个时候如果采用 \(O(k^2)\) 暴力先 \(\ln\)\(\exp\) 求快速幂的话复杂度为 \(O(2^cck^3)\)。如果使用牛顿迭代的话因为常数原因,而且多项式次数较小跑得还没有暴力快。

对于某个 \(k\) 元组来说,考虑钦定一个 \(y\),再对 \(x\) 这个维度做 FWT。因为钦定了 \(y\) 的话只会有一个 \(x\) 的系数是形如 \(\frac{y^k}{k!}\) 的。根据 FWT 的相关理论,做完 FWT 之后中每一项都是形如 \(\pm\frac{y^k}{k!}\) 的。

这个时候就可以将每一个 \(k\) 元组对应到一个 \(1\)\(-1\) 的序列上去,这个序列可以状压。将状压之后的东西放在一起快速幂即可。

代码

#include<bits/stdc++.h>
#pragma GCC optimize("Ofast,unroll-loops")
#define pb emplace_back
#define popc __builtin_popcount
using namespace std;
typedef int ll;
typedef long long int li;
typedef vector<ll> vi;
const ll MAXN=65541,MOD=998244353;
map<vi,ll>mp;
ll n,kk,c,xorv,tot,fct,d,tp;
ll x[MAXN],invl[MAXN],finv[MAXN],f[MAXN],cnt[MAXN<<1];
ll st[201],sm[201],tmp[21],tmp2[21],tmp3[21];
li tmp4[21];
vi v[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline ll qpow(ll base,ll exponent)
{
    ll res=1;
    while(exponent)
    {
        if(exponent&1)
        {
            res=(li)res*base%MOD;
        }
        base=(li)base*base%MOD,exponent>>=1;
    }
    return res;
}
inline void ln(ll fd,ll *f,ll *res)
{
    li r;
    res[0]=0;
    for(register int i=1;i<fd;i++)
    {
        r=0;
        for(register int j=1;j<i;j++)
        {
            r+=(li)f[j]*res[i-j]%MOD;
        }
        res[i]=((li)i*f[i]%MOD-r%MOD+MOD)%MOD;
    }
    for(register int i=1;i<fd;i++)
    {
        res[i]=(li)res[i]*invl[i]%MOD;
    }
}
inline void exp(ll fd,ll *f,ll *res)
{
    li r;
    res[0]=1;
    for(register int i=1;i<fd;i++)
    {
        r=0,f[i]=(li)f[i]*i%MOD;
        for(register int j=0;j<i;j++)
        {
            r+=(li)f[j+1]*res[i-j-1]%MOD;
        }
        res[i]=r%MOD*invl[i]%MOD;
    }
}
int main()
{
    n=read(),kk=read(),c=read(),invl[0]=invl[1]=finv[0]=finv[1]=fct=1;
    for(register int i=2;i<65536;i++)
    {
        invl[i]=(MOD-(li)(MOD/i)*invl[MOD%i]%MOD)%MOD;
        finv[i]=(li)finv[i-1]*invl[i]%MOD;
    }
    for(register int i=1;i<=n;i++)
    {
        xorv^=(x[i]=read());
        for(register int j=0;j<=kk;j++)
        {
            v[i].pb(x[i]^(x[i]-j));
        }
        mp[v[i]]++;
    }
    for(auto i:mp)
    {
        v[++tot]=i.first,sm[tot]=i.second;
    }
    for(register int i=2;i<=kk;i++)
    {
        fct=(li)fct*i%MOD;
    }
    for(register int s=0;s<(1<<c);s++)
    {
        for(register int i=1;i<=tot;i++)
        {
            d=0;
            for(register int j=0;j<=kk;j++)
            {
                d|=((popc(s&v[i][j])&1)<<j);
            }
            !cnt[d]?st[++tp]=d:1,cnt[d]+=sm[i];
        }
        memset(tmp,0,sizeof(tmp)),tmp[0]=1;
        for(register int i=1;i<=tp;i++)
        {
            for(register int j=0;j<=kk;j++)
            {
                tmp2[j]=(st[i]&(1<<j))?MOD-finv[j]:finv[j];
            }
            ln(kk+1,tmp2,tmp3);
            for(register int j=0;j<=kk;j++)
            {
                tmp3[j]=(li)tmp3[j]*cnt[st[i]]%MOD;
            }
            exp(kk+1,tmp3,tmp2),memset(tmp4,0,sizeof(tmp4));
            for(register int j=0;j<=kk;j++)
            {
                for(register int k=0;k<=kk-j;k++)
                {
                    tmp4[j+k]+=(li)tmp2[j]*tmp[k];
                }
            }
            for(register int j=0;j<=kk;j++)
            {
                tmp[j]=tmp4[j]%MOD;
            }
            cnt[st[i]]=0;
        }
        tp=0,f[s]=(li)tmp[kk]*fct%MOD;
    }
    for(register int i=1;i<(1<<c);i<<=1)
    {
        for(register int p=0;p<(1<<c);p+=i<<1)
        {
            for(register int j=p;j<p+i;j++)
            {
                d=f[j],f[j]=(f[j]+f[j+i])%MOD,f[j+i]=(d-f[j+i]+MOD)%MOD;
            }
        }
    }
    d=(li)qpow(1<<c,MOD-2)*qpow(n,MOD-1-kk)%MOD;
    for(register int i=0;i<(1<<c);i++)
    {
        printf("%d ",(li)f[i^xorv]*d%MOD);
    }
}
posted @ 2020-10-21 22:07  Karry5307  阅读(157)  评论(0编辑  收藏  举报