【Unknown Source】异或和
Descripition
给定 ,求在 中选择 个互不相同的数字满足其异或和为 的方案数 的结果
非常大,给出其二进制下表示中的所有 的位置:共 个从低到高给出的 ,其中
Solution
先求有多少选择数的排列,最后除掉阶乘
考虑一类集合划分容斥,将所有元素分成若干集合,每个集合内部数字相等,对这些集合划分的方式赋以一定的系数使得合法者贡献 次,非法者贡献 次
这个模型和 是一样的,将所有元素互不相等的关系转化如下
由于排列中任意两个元素都可能相等,所以可以将元素自身看做一个点,元素之间的相等关系看做边
取出全部边集的一个子集 ,强制每个联通块中的元素数值相等,联通块之间不强制不相等,并要求所有元素的 和为 ,贡献系数是 ,方案数 是给元素分配数值使其满足相等关系且异或和为 的方案数
关于该容斥系数的正确性
如果图上不存在边(也就是所有元素都相等,是一个合法方案)那么选择边表子集的方案数为
否则有元素相等,不合法,需要证明其所有方案的系数之和为 ,可以找到图上存在的一条边 ,对于所有其它边的选取方案,选择 和不选择 会导致奇偶性不同,系数正负 消去了
注意到 的值只和 中包含的奇数联通块的个数有关,那么所有的 可以归为对于 计算选择 个不要求不等且都 的数字使得异或和为
考虑对于单个 ,枚举这些数字和 的 以及这个位置上有多少个数字选了 :
如果这位选择了 ,后面选数字的方案是后缀位上权值和 ,选择 的数字中有一个用来最后进行抵消,另外的数字选择方案是当前位的权值 ,方案数即:
暴力求解 个这样的式子就前功尽弃,由于每位的 是固定的,变化的只有 ,尝试计算
使用分治乘法进行通分得到完整多项式,对 做一样的通分即可
注意 是奇数时 只能是 ,直接根据上面的公式来计算即可
要求出来答案,避不开的是对于每个 求出来有几个 满足有 个联通块大小为奇数
先考察所有选择边将 个点联通的方式 中, 之和 ,注意到
时,用全部方案减去不连通方案,全部方案的权值和上面提及了是 ,不连通则枚举 所在联通块的大小
但是在外部点数 的情况下给它们任意连边的所有方案权值和是
只有一个点时选点有 种方案,每个点对应的权值之和都是 ,所以有 ,得到
那么设 为选出一个大小为奇数的集合的 ,同时设 为所有偶数大小集合的 ,使用上面所说的容斥系数可以得到下面的式子
直接使用 表示 的复合逆,根据复合逆定义有:
令 ,有
套用扩展拉格朗日反演公式:
由于所需为一个关于 的多项式且每项都有 ,那么需要继续进行和式变换
求解负指数次方是很困难的,但是注意到 的常数项为 所以可以上下同时除以
一步一步求复杂度就是 的
注意到在进行 的乘方运算时每个 之间没有顺序,所以要除掉
最后统计答案直接将两个部分得到的权值对位乘起来相加即可
卡常注意在分治乘法时不要使用 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律