Descripition
给定 \(R,n\),求在 \([0,R)\) 中选择 \(n\) 个互不相同的数字满足其异或和为 \(0\) 的方案数 \(\mod 998244353\) 的结果
\(R\) 非常大,给出其二进制下表示中的所有 \(1\) 的位置:共 \(K\) 个从低到高给出的 \(a_i\),其中 \(\max a_i\le 1145141919810\)
\(n,K\le 114514\)
Solution
先求有多少选择数的排列,最后除掉阶乘
考虑一类集合划分容斥,将所有元素分成若干集合,每个集合内部数字相等,对这些集合划分的方式赋以一定的系数使得合法者贡献 \(1\) 次,非法者贡献 \(0\) 次
这个模型和 \(\rm ABC236 Ex\) 是一样的,将所有元素互不相等的关系转化如下
由于排列中任意两个元素都可能相等,所以可以将元素自身看做一个点,元素之间的相等关系看做边
取出全部边集的一个子集 \(E\),强制每个联通块中的元素数值相等,联通块之间不强制不相等,并要求所有元素的 \(\oplus\) 和为 \(0\) ,贡献系数是 \((-1)^{|E|}\),方案数 \(f(E)\) 是给元素分配数值使其满足相等关系且异或和为 \(0\) 的方案数
关于该容斥系数的正确性
如果图上不存在边(也就是所有元素都相等,是一个合法方案)那么选择边表子集的方案数为 \(1\)
否则有元素相等,不合法,需要证明其所有方案的系数之和为 \(0\),可以找到图上存在的一条边 \(e\),对于所有其它边的选取方案,选择 \(e\) 和不选择 \(e\) 会导致奇偶性不同,系数正负 \(1\) 消去了
注意到 \(f(E)\) 的值只和 \(E\) 中包含的奇数联通块的个数有关,那么所有的 \(f(E)\) 可以归为对于 \(k\in[0,n]\) 计算选择 \(k\) 个不要求不等且都 \(\in[0,R)\) 的数字使得异或和为 \(0\)
考虑对于单个 \(k\),枚举这些数字和 \(R\) 的 \(\rm LCP\) 以及这个位置上有多少个数字选了 \(1\) :
如果这位选择了 \(1\),后面选数字的方案是后缀位上权值和 \(s\),选择 \(0\) 的数字中有一个用来最后进行抵消,另外的数字选择方案是当前位的权值 \(b\),方案数即:
暴力求解 \(\Theta(nK)\) 个这样的式子就前功尽弃,由于每位的 \(b,s\) 是固定的,变化的只有 \(k\),尝试计算 \(\sum\limits_{i\ge0} x^{i}\sum\limits_{j=0}^{K-1}\dfrac{s_j^i}{2b_j}=\sum\limits_{j=0}^{K-1}\dfrac{s_j^i}{2b_j(1-x)}\)
使用分治乘法进行通分得到完整多项式,对 \((b\pm s)^k\) 做一样的通分即可
注意 \(k\) 是奇数时 \(\rm LCP\) 只能是 \(0\),直接根据上面的公式来计算即可
要求出来答案,避不开的是对于每个 \(k\in[0,n]\) 求出来有几个 \(f(E)\) 满足有 \(k\) 个联通块大小为奇数
先考察所有选择边将 \(x\) 个点联通的方式 \(\{E\}\) 中,\((-1)^{|E|}\) 之和 \(h(x)\),注意到 \(h(1)=1\)
\(x>1\) 时,用全部方案减去不连通方案,全部方案的权值和上面提及了是 \(0\) ,不连通则枚举 \(1\) 所在联通块的大小
但是在外部点数 \(>1\) 的情况下给它们任意连边的所有方案权值和是 \(0\)
只有一个点时选点有 \(x-1\) 种方案,每个点对应的权值之和都是 \(h(x-1)\) ,所以有 \(h(x)=-(x-1)h(x-1)\) ,得到 \(h(n)=(-1)^{n-1}(n-1)!\)
那么设 \(F(x)\) 为选出一个大小为奇数的集合的 \(\rm EGF\),同时设 \(G(x)\) 为所有偶数大小集合的 \(\rm EGF\) ,使用上面所说的容斥系数可以得到下面的式子
直接使用 \(F^{-1}(x)\) 表示 \(F(x)\) 的复合逆,根据复合逆定义有:
令 \(H(x)=G(F^{-1}(x))\),有 \(H(F(x))=G(x)\)
套用扩展拉格朗日反演公式:
由于所需为一个关于 \(y\) 的多项式且每项都有 \(x^n\),那么需要继续进行和式变换
求解负指数次方是很困难的,但是注意到 \(F^{-1}(x)\) 的常数项为 \(0\) 所以可以上下同时除以 \(x^{n}\)
一步一步求复杂度就是 \(\Theta(n\log n)\) 的
注意到在进行 \(F\) 的乘方运算时每个 \(F\) 之间没有顺序,所以要除掉 \(i!\)
最后统计答案直接将两个部分得到的权值对位乘起来相加即可
卡常注意在分治乘法时不要使用 Poly::Mul
而是展开 NTT
Code
#pragma GCC target("avx")
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
const int N=6e5+10;
int fac[N],ifac[N],inv[N];
int n,K,R;
namespace PART1{
inline poly get_revF(){
int pw2=1;
vector<int> vec1,vec2;
vec1.resize(n+1); vec2.resize(n+1);
for(int i=0;i<=n;++i){
vec1[i]=vec2[i]=mul(ifac[i],pw2);
ckadd(pw2,pw2);
}
ckadd(vec1[0],1); ckdel(vec2[0],1);
vec1=Inv(vec1,n+1);
vec2=Mul(vec2,vec1);
vec2.resize(n+1);
return vec2;
}
vector<int> revF;
inline poly get_H(){
vector<int> vec=revF;
vec=Mul(vec,vec);
vec.resize(n+1);
for(auto &t:vec) t=del(0,t);
ckadd(vec[0],1);
return qpow(vec,mul(R,inv[2]));
}
poly main(){
revF=get_revF();
vector<int> H=get_H();
revF.erase(revF.begin());
revF=qpow(revF,n);
revF.resize(n+1);
revF=Inv(revF,n+1);
vector<int> dH=deriv(H);
poly M=Mul(H,revF);
poly dM=Mul(dH,revF);
M.resize(n+1); dM.resize(n);
vector<int> ans={dM[n-1]};
ans.resize(n+1);
for(int i=1;i<=n;++i){
int val1=i==n?0:dM[n-1-i];
int val2=mul(i,M[n-i]);
ans[i]=add(val1,val2);
ckmul(ans[i],ifac[i]);
}
for(auto &t:ans) ckmul(t,inv[n]);
return ans;
}
}
namespace PART2{
vector<int> A,T;
inline pair<vector<int>,vector<int> >solve(int l,int r){
if(l==r)return {{1},{T[l],del(0,mul(A[l],T[l]))}};
int mid=(l+r)>>1;
pair<poly,poly> a=solve(l,mid),b=solve(mid+1,r);
pair<poly,poly> c;
int lim=1; while(lim<=a.sec.size()+b.sec.size()) lim<<=1;
NTT(a.sec,lim,1);
NTT(b.sec,lim,1);
NTT(a.fir,lim,1);
NTT(b.fir,lim,1);
c.fir.resize(lim);
c.sec.resize(lim);
for(int i=0;i<lim;++i){
c.fir[i]=add(mul(a.fir[i],b.sec[i]),mul(b.fir[i],a.sec[i]));
c.sec[i]=mul(a.sec[i],b.sec[i]);
}
NTT(c.fir,lim,-1); NTT(c.sec,lim,-1);
while(c.fir.size()>1&&!c.fir.back()) c.fir.pop_back();
while(c.sec.size()>1&&!c.sec.back()) c.sec.pop_back();
return c;
}
int t[N],s[N];
poly main(){
for(int i=1;i<=K;++i) s[i]=add(s[i-1],t[i]=ksm(2,a[i]%(mod-1)));
A.resize(K); T.resize(K);
for(int i=1;i<=K;++i){
A[i-1]=del(t[i],s[i-1]);
T[i-1]=add(t[i],t[i]);
}
pair<poly,poly> tmp=solve(0,K-1);
tmp.sec.resize(n+1);
vector<int> vec1=Mul(tmp.fir,Inv(tmp.sec,n+1));;
for(int i=1;i<=K;++i){
A[i-1]=add(t[i],s[i-1]);
T[i-1]=add(t[i],t[i]);
}
tmp=solve(0,K-1);
tmp.sec.resize(n+1);
vector<int> vec2=Mul(tmp.fir,Inv(tmp.sec,n+1));;
for(int i=1;i<=K;++i){
A[i-1]=s[i-1];
T[i-1]=add(t[i],t[i]);
}
tmp=solve(0,K-1);
tmp.sec.resize(n+1);
vector<int> vec3=Mul(tmp.fir,Inv(tmp.sec,n+1));;
vec1.resize(n+1); vec2.resize(n+1); vec3.resize(n+1);
vector<int> ans=Plus(vec2,vec1);
for(int i=0;i<=n;++i){
ckdel(ans[i],vec3[i]);
if(i&1) ckadd(ans[i],vec3[i]);
else ckdel(ans[i],vec3[i]);
}
for(int i=1;i<=n;i+=2){
int S=s[K-1],T=t[K];
int coef=ksm(add(T,T),mod-2);
int val=add(ksm(del(T,S),i),ksm(add(T,S),i));
if(!(i&1)) ckadd(val,mul(2,ksm(S,i)));
ans[i]=mul(coef,val);
}
ans[0]=1;
return ans;
}
}
signed main(){
freopen("xor.in","r",stdin); freopen("xor.out","w",stdout);
n=6e5; fac[0]=inv[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i),inv[i]=mul(ifac[i],fac[i-1]);
n=read(); K=read();
for(int i=1;i<=K;++i)a[i]=read<ll>(),ckadd(R,ksm(2,a[i]%(mod-1)));
vector<int> res1=PART1::main();
vector<int> res2=PART2::main();
int ans=0;
for(int i=0;i<=n;++i) ckadd(ans,mul(res1[i],res2[i]));
print(ans);
return 0;
}