这是原题链接,但是寄掉了
题目描述
给出 n n n 个 0 到 K − 1 K − 1 K−1 之间的整数,对于每个 0 ≤ i ≤ k − 1 0 ≤ i ≤ k − 1 0≤i≤k−1 求从中选 m m m 个使得异或和为 i i i 的方案数对 998244353 取模。
对于 10% 的数据,
n
,
K
≤
128
n, K ≤ 128
n,K≤128;
对于 20% 的数据,
n
,
K
≤
2048
n, K ≤ 2048
n,K≤2048;
对于 40% 的数据,
n
,
K
≤
8192
n, K ≤ 8192
n,K≤8192;
对于另外 10% 的数据 ,
m
=
1
m = 1
m=1;
对于另外 10% 的数据,
m
=
2
m = 2
m=2;
对于另外 20% 的数据,
m
≤
100
m ≤ 100
m≤100;
对于 100% 的数据,
1
≤
m
≤
n
<
130000
,
K
≤
2
17
1 ≤ m ≤ n < 130000, K ≤ 2^{17}
1≤m≤n<130000,K≤217,保证
K
K
K 是 2 的正整数次幂。
题解
首先想到可以对每个元素列一个大小为 k k k 的 FWT 点值数组,然后答案的点值即为这 n n n 个点值中任选 m m m 个相乘的和,最后逆变换把点值转换回来即可,可以做到 O ( n m k ) O(nmk) O(nmk)。
然后发现每个位置的点值只可能是 1 或 -1,如果我们求出每个位置有多少个1和-1,那么就有更快的做法。假设 i i i 处的 1 的数量为 c i c_i ci,-1 的数量为 d i d_i di,由于 c i + d i = n c_i+d_i=n ci+di=n,我们再用一次普通正常的 FWTXOR 求出 c i − d i c_i-d_i ci−di 即可。
1 和 -1 的个数搞定了,然后发现我们要求的其实是这么个东西: [ x m ] ( 1 + x ) c i ( 1 − x ) n − c i [x^m](1+x)^{c_i}(1-x)^{n-c_i} [xm](1+x)ci(1−x)n−ci,一个多项式上的某一位的系数。
剩下的就比较技巧了。考虑对 0 ∼ n 0\sim n 0∼n 的每一种 c i c_i ci 求出 [ x m ] ( 1 + x ) c i ( 1 − x ) n − c i [x^m](1+x)^{c_i}(1-x)^{n-c_i} [xm](1+x)ci(1−x)n−ci 的值,我们可以对 0 ∼ n 0\sim n 0∼n 分治往下递归来解决这个问题。
假设递归到了 [ l , r ] [l,r] [l,r] 这个区间,那么当前计算出的多项式是 ( 1 + x ) l ( 1 − x ) n − r (1+x)^l(1-x)^{n-r} (1+x)l(1−x)n−r,我们需要对其乘上 ( 1 − x ) r − m i d (1-x)^{r-mid} (1−x)r−mid 来转移到左区间 [ l , m i d ] [l,mid] [l,mid]。
朴素的多项式乘法显然是 O ( n 2 log n ) O(n^2\log n) O(n2logn) 的,因为每次计算的多项式的项数是 O ( n ) O(n) O(n) 级别。但是由于我们只求 x m x^m xm 这一项的系数,所以只保留有用的项即可。举例子,假如我们递归到了区间 [ l , r ] [l,r] [l,r],那么往下递归最多会再乘上 r − l r-l r−l 个 ( 1 + x ) (1+x) (1+x) 或 ( 1 − x ) (1-x) (1−x),这个时候次数小于 m − ( r − l ) m-(r-l) m−(r−l) 或大于 m m m 的项肯定不会被卷积进答案里,所以多项式最多只用保留 r − l + 1 r-l+1 r−l+1 项。这个时候多项式长度等于区间长度,故复杂度是 O ( n log 2 n ) O(n\log^2n) O(nlog2n)。
代码
这份代码敲大常的~
#include<bits/stdc++.h>//JZM yyds!!
#define ll long long//God JZM!!
#define lll __int128//JZM RollInDark!!
#define uns unsigned
#define fi first
#define se second
#define IF (it->fi)
#define IS (it->se)
#define lowbit(x) ((x)&-(x))
#define END putchar('\n')
#define inline jzmyyds
using namespace std;
const int MAXN=(1<<19)+5;
const ll INF=1e18;
ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int ptf[50],lpt;
void print(ll x,char c='\n'){
if(x<0)putchar('-'),x=-x;
ptf[lpt=1]=x%10;
while(x>9)x/=10,ptf[++lpt]=x%10;
while(lpt)putchar(ptf[lpt--]^48);
if(c>0)putchar(c);
}
const ll MOD=998244353,iv2=(MOD+1)>>1;
ll ksm(ll a,ll b,ll mo){
ll res=1;
for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
return res;
}
ll fac[MAXN<<1],inv[MAXN<<1];
ll init(int n){
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)fac[i]=fac[i-1]*i%MOD;
inv[n]=ksm(fac[n],MOD-2,MOD);
for(int i=n-1;i>1;i--)inv[i]=inv[i+1]*(i+1)%MOD;
return 114514;
}
int cbddl=init(MAXN);
ll C(int n,int m){
if(m>n||m<0)return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
#define g 3ll
int rev[MAXN];
ll omg[MAXN];
int NTT(ll*a,int N,int inv){
int bit=1,n=N;
while((1<<bit)<n)bit++;
n=(1<<bit);
for(int i=0;i<n;i++){
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
if(i<rev[i])swap(a[i],a[rev[i]]);
}ll x,y,tmp;omg[0]=1;
for(int m=1,mi=(MOD-1)>>1;m<n;m<<=1,mi>>=1){
tmp=ksm(g,inv>0?mi:MOD-1-mi,MOD);
for(int i=1;i<m;i++)omg[i]=omg[i-1]*tmp%MOD;
for(int i=0,om=0;i<n;i+=(m<<1),om=0)
for(int j=i;j<i+m;j++,om++)
x=a[j],y=a[j+m]*omg[om]%MOD,a[j]=(x+y)%MOD,a[j+m]=(x+MOD-y)%MOD;
}if(inv<0)for(int i=0,iv=ksm(n,MOD-2,MOD);i<n;i++)(a[i]*=iv)%=MOD;
return n;
}
#undef g
void FWTXOR(ll*a,int n,int inv){
ll cg=inv>0?1:iv2,x=2333;
for(int k=1;k<n;k<<=1)
for(int i=0;i<n;i+=(k<<1))
for(int j=i;j<i+k;j++)
x=a[j],a[j]=(x+a[j+k])*cg%MOD,a[j+k]=(x+MOD-a[j+k])*cg%MOD;
}
int n,m,k;
ll a[MAXN],f[MAXN];
vector<int>qu[MAXN];
void solve(int l,int r,ll*f,int m){
if(l==r){
for(int x:qu[l])a[x]=f[m];
return;
}int mid=(l+r)>>1,len=min(m,r-l+1);
for(int i=0;i<=len;i++)f[i]=f[i+m-len];
for(int i=len+1;i<=m;i++)f[i]=0;
int N=NTT(f,(len+1)<<1,1);
ll g[N+5];
memset(g,0,sizeof(g));
for(int i=0;i<=min(len,r-mid);i++)g[i]=((i&1)?(MOD-C(r-mid,i)):C(r-mid,i));
NTT(g,N,1);
for(int i=0;i<N;i++)(g[i]*=f[i])%=MOD;
NTT(g,N,-1);
for(int i=len+1;i<N;i++)g[i]=0;
solve(l,mid,g,len);
memset(g,0,sizeof(g));
for(int i=0;i<=min(len,mid+1-l);i++)g[i]=C(mid+1-l,i);
NTT(g,N,1);
for(int i=0;i<N;i++)(g[i]*=f[i])%=MOD;
NTT(g,N,-1);
for(int i=len+1;i<N;i++)g[i]=0;
solve(mid+1,r,g,len);
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)a[read()]++;
FWTXOR(a,k,1);
for(int i=0;i<k;i++)qu[(a[i]+n)%MOD>>1].push_back(i);
f[0]=1,solve(0,n,f,m);
FWTXOR(a,k,-1);
for(int i=0;i<k;i++)print(a[i],i<k-1?' ':'\n');
return 0;
}