Codeforces 1408I - Bitwise Magic(找性质+集合幂级数)
Yet another immortal D1+D2 I %%%%%%
首先直接统计肯定是非常不容易的,不过注意到这个 \(k\) 非常小,因此考虑对这个 \(k\) 做点文章。我们考虑每个数被执行了多少次 \(-1\) 操作,设第 \(i\) 个数被执行了 \(b_i\) 次 \(-1\) 操作,那么最终的结果就是 \((a_1-b_1)\oplus(a_2-b_2)\oplus\cdots\oplus(a_n-b_n)\)。然后就是比较神仙的一步了,注意到这个 \(b_i\) 也是 \(\le k\) 的,也就是说我们感性地理解一下这个 \(a_i-b_i\) 差距应该不是非常大,因此我们考虑将原序列进行一个增量转化,即我们考虑统计 \(ans’_i\) 表示有多少种操作序列,满足 \((a_1\oplus(a_1-b_1))\oplus(a_2\oplus(a_2-b_2))\oplus(a_3\oplus(a_3-b_3))\oplus\cdots\oplus(a_n\oplus(a_n-b_n))=i\),那么最终真正的 \(ans_i=ans’_{sum\oplus i}·\dfrac{1}{n^k}\),其中 \(sum=a_1\oplus a_2\oplus\cdots\oplus a_n\)。
为什么我们要做这么一个奇怪的操作呢?是为了接下来更加神仙的操作做铺垫。我们考虑生成函数,由于这里涉及异或和和操作次数两维,因此我们需要二维生成函数,具体来说我们按照 NFLSOJ #1072 的套路,\(x\) 那一维的指数做异或和,\(y\) 那一维的指数做加法卷积,由于操作的本质是排列,因此我们需要 EGF。我们考虑构造生成函数 \(F_i(x,y)=1+\sum\limits_{j=1}^k\dfrac{1}{j!}x^{b_{i,j}}y^j\),其中 \(b_{i,j}=a_i\oplus(a_i-j)\),这样我们将所有 \(F_i(x,y)\) 卷起来得到幂级数 \(H(x,y)\),那么根据 EGF 那一套理论显然有 \(ans’_i=k![x^iy^k]H(x,y)\)。根据子集卷积的方法,我们考虑将每个幂级数写成 \(\sum\limits_{j=0}^{2^c-1}G_j(y)x^j\) 的形式,这样我们定义的幂级数的乘法就可以看成,对两个系数是形式幂级数的集合幂级数做 xor 卷积,这样我们 FWTxor 一下 \(x\) 那一维就独立了,这样我们每一维上的多项式相乘,再 IFWTxor 回去即可得到真正的系数,相信深入了解过集合幂级数的同学们这一步都不难理解。这样我们就得到了跑不过暴力的 \(4^cc^2\) 的做法。
考虑优化,这时候就要用到我们之前说的“神仙的操作了”,由于 \(k\) 很小,我们猜测本质不同的 \(b_{i,j}\) 个数也不多,事实上的确如此。我们记序列 \(d_i=\{i\oplus(i-1),i\oplus(i-2),\cdots,i\oplus(i-k)\}\),那么有一个结论,本质不同的 \(d_i\) 个数为 \(\mathcal O(ck)\) 级别,打个表可以发现 \(k=c=16\) 时这个数目为 \(192\)。证明大概就你考虑 \(\text{lowbit}(i)\),如果 \(\text{lowbit}(i)>k\),那么显然 \(i-1\) 与 \(i-k\) 在 \(\text{lowbit}(i)\) 以上的位都相同,也就是说 \(d_i\) 只与 \(\text{lowbit}(i)\) 有关,只有 \(\mathcal O(c)\) 种取值,否则后 \(\log_2(k)\) 位的取值肯定是影响 \(d_i\) 的值的,这样就有 \(2^{\log_2(k)}=\mathcal O(k)\) 种取值,第 \(\log_2(k)\) 位以上第一个 \(1\) 的位置也对 \(d_i\) 的取值有影响,其他位置都对 \(d_i\) 的取值产生不了任何影响,因此总共只有 \(ck\) 种取值,证毕。
显然对于 \(d_{a_i}=d_{a_j}\) 的两个下标 \(i,j\),必然也有 \(F_{i}(x,y)=F_j(x,y)\),因此我们可以统一对所有本质不同的 \(F_i(x,y)\) 算一遍答案。也就是说我们根据 \(d_i\) 将所有数划分成 \(\mathcal O(ck)\) 个等价类,假设有 \(cnt_i\) 个 \(a_j\) 属于第 \(i\) 个等价类,那么答案应乘上 \(G_i(x,y)^{cnt_i}\),其中 \(G_i(x,y)\) 表示第 \(i\) 个等价类对应的 \(F_i(x,y)\)。至于怎样计算 \(G_i(x,y)\)……大概就对于每一维 FWTxor 一下然后做一遍多项式快速幂再 IFWTxor 回去,多项式快速幂可以通过取 \(\ln\) 再 \(\exp\) 回去的方法,由于这题数据范围很小,NTT 反而比暴力慢,故我们应选择暴力求 \(\ln,\exp\),此时复杂度大概是 \(2^cck^3=n\log^4n\),无法通过此题。
考虑优化,注意到在我们对每一位 FWTxor 之后,每一位上的多项式 \([y^j]\) 的系数肯定是 \(\pm\dfrac{1}{y^j}\),由于 \(k\) 很小,因此 FWTxor 之后每一位的多项式总共只有 \(2^k\) 种可能,是在我们能够接受的,因此我们考虑对全部 \(2^k\) 种多项式预处理它们的 \(\ln\),这部分复杂度是 \(2^kk^2\) 的,然后对于每一个等价类,我们对每一维调用预处理的 \(\ln\) 值时,不立即 \(\exp\) 回去,而是直接令当前位多项式的 \(\ln\) 加上 \(cnt_i·\ln(G_i(y))\),最后再统一 \(\exp\) 回去,这样复杂度就降到了 \(2^cc^3=n\log^3n\)。
卡时限过题真 nm 有趣
const int MAXN=16;
const int MAXP=65536;
const int MAXC=192;
const u64 BS=17;
const int INV2=499122177;
const int MOD=998244353;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,k,c,fac[MAXP+5],ifac[MAXP+5],inv[MAXP+5],a[MAXP+5];
void init_fac(int n){
for(int i=(fac[0]=ifac[0]=inv[0]=inv[1]=1)+1;i<=n;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*inv[i]%MOD;
}
int b[MAXC+5][MAXN+5],idcnt,cnt[MAXC+5],bel[MAXP+5];
map<u64,int> id;
void FWTxor(int *a,int len,int type){
for(int i=2;i<=len;i<<=1)
for(int j=0;j<len;j+=i)
for(int k=0;k<(i>>1);k++){
int X=a[j+k],Y=a[(i>>1)+j+k];
a[j+k]=1ll*type*(X+Y)%MOD;
a[(i>>1)+j+k]=1ll*type*(X-Y+MOD)%MOD;
}
}
void getln(int *a,int *b,int len){
b[0]=0;
for(int i=1;i<=len;i++){
b[i]=1ll*a[i]*i%MOD;
for(int j=1;j<i;j++) b[i]=(b[i]-1ll*j*b[j]%MOD*a[i-j]%MOD+MOD)%MOD;
b[i]=1ll*b[i]*inv[i]%MOD;
}
}
int tmp[MAXN+5][MAXP+5],res[MAXP+5];
void getexp(int *a,int *b,int len){
b[0]=1;
for(int i=1;i<=len;i++){
b[i]=0;
for(int j=1;j<=i;j++) b[i]=(b[i]+1ll*b[i-j]*j%MOD*a[j])%MOD;
b[i]=1ll*b[i]*inv[i]%MOD;
}
}
void calc_hash(){
for(int i=k;i<(1<<c);i++){
u64 pw=1,hs=0;
for(int j=1;j<=k;j++){
pw=pw*BS;hs+=(i^(i-j))*pw;
} if(!id[hs]){
id[hs]=++idcnt;
for(int j=1;j<=k;j++) b[idcnt][j]=i^(i-j);
} bel[i]=id[hs];
// printf("%llu\n",hs);
} for(int i=1;i<=n;i++) cnt[bel[a[i]]]++;
}
int lnb[MAXP+5][MAXN+5],tt[MAXP+5][MAXN+5];
void init_ln(){
for(int i=0;i<(1<<k);i++){
static int pl[MAXN+5]={0};
memset(pl,0,sizeof(pl));pl[0]=1;
for(int j=1;j<=k;j++) pl[j]=((i>>(j-1)&1)?ifac[j]:(MOD-ifac[j]));
// printf("f=");for(int j=0;j<=k;j++) printf("%d%c",pl[j]," \n"[j==k]);
getln(pl,lnb[i],k);
// printf("ln=");for(int j=0;j<=k;j++) printf("%d%c",lnb[i][j]," \n"[j==k]);
}
}
void calc_num(){
for(int i=1;i<=idcnt;i++){
memset(tmp,0,sizeof(tmp));
for(int j=1;j<=k;j++) tmp[j][b[i][j]]=1;
for(int j=1;j<=k;j++) FWTxor(tmp[j],1<<c,1);
// printf("dealing %d\n",i);
// for(int j=1;j<=k;j++) for(int l=0;l<(1<<c);l++)
// printf("%d%c",tmp[j][l]," \n"[l==((1<<c)-1)]);
for(int j=0;j<(1<<c);j++){
int msk=0;
for(int l=1;l<=k;l++)
if(tmp[l][j]==1) msk|=(1<<l-1);
for(int l=1;l<=k;l++) tt[j][l]=(tt[j][l]+1ll*lnb[msk][l]*cnt[i])%MOD;
// printf("%d %d %d\n",i,j,msk);
} //printf("%d\n",cnt[i]);
}
}
int main(){
scanf("%d%d%d",&n,&k,&c);int sm=0;init_fac(MAXP);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sm^=a[i];
calc_hash();init_ln();calc_num();
for(int i=0;i<(1<<c);i++){
static int pl[MAXN+5]={0};
memset(pl,0,sizeof(pl));
getexp(tt[i],pl,k);
res[i]=pl[k];
// for(int j=0;j<=k;j++) printf("%d%c",tt[i][j]," \n"[j==k]);
// for(int j=0;j<=k;j++) printf("%d%c",pl[j]," \n"[j==k]);
// printf("%d\n",res[i]);
} FWTxor(res,1<<c,INV2);
// for(int i=0;i<(1<<c);i++) printf("%d%c",res[i]," \n"[i==((1<<c)-1)]);
int coef=1ll*fac[k]*qpow(qpow(n,MOD-2),k)%MOD;
for(int i=0;i<(1<<c);i++) printf("%d%c",1ll*coef*res[sm^i]%MOD," \n"[i==((1<<c)-1)]);
return 0;
}
Codeforces 1408I - Bitwise Magic(找性质+集合幂级数)的更多相关文章
- CodeForces 1408I Bitwise Magic
题意 给定三个整数 \(n,k,c\) 和一个长度为 \(n\) 的序列 \(a\),保证 \(a_i\) 互不相同.可以操作 \(k\) 次,每次随机选择一个 \(a_i\) 变成 \(a_i-1\ ...
- Codeforces 1442D - Sum(找性质+分治+背包)
Codeforces 题面传送门 & 洛谷题面传送门 智商掉线/ll 本来以为是个奇怪的反悔贪心,然后便一直往反悔贪心的方向想就没想出来,看了题解才发现是个 nb 结论题. Conclusio ...
- bzoj 4036 集合幂级数
集合幂级数其实就是一种集合到数的映射,并且我们针对集合的一些操作(or xor and specil or )为这种映射定义运算.其中一些东西可以通过某些手段将其复杂度降低. orz vfk /** ...
- 【uoj#94】【集训队互测2015】胡策的统计(集合幂级数)
题目传送门:http://uoj.ac/problem/94 这是一道集合幂级数的入门题目.我们先考虑求出每个点集的连通生成子图个数,记为$g_S$,再记$h_S$为点集$S$的生成子图个数,容易发现 ...
- 【杂题】[AGC034F] RNG and XOR【集合幂级数】【FWT】【DP】
Description 你有一个随机数生成器,它会以一定的概率生成[0,2^N-1]中的数,每一个数的概率是由序列A给定的,Pi=Ai/sum(Ai) 现在有一个初始为0的数X,每一轮随机生成一个数v ...
- 洛谷 P6570 - [NOI Online #3 提高组] 优秀子序列(集合幂级数+多项式)
洛谷题面传送门 首先 \(3^n\) 的做法就不多说了,相信对于会状压 dp+会枚举子集的同学来说不算困难(暴论),因此这篇博客将着重讲解 \(2^nn^2\) 的做法. 首先如果我们把每个 \(a_ ...
- Atcoder Grand Contest 008 E - Next or Nextnext(乱搞+找性质)
Atcoder 题面传送门 & 洛谷题面传送门 震惊,我竟然能独立切掉 AGC E 难度的思维题! hb:nb tea 一道 感觉此题就是找性质,找性质,再找性质( 首先看到排列有关的问题,我 ...
- 洛谷 P6775 - [NOI2020] 制作菜品(找性质+bitset 优化 dp)
题面传送门 好久没写过题解了,感觉几天没写手都生疏了 首先这种题目直接做肯定是有些困难的,不过注意到题目中有个奇奇怪怪的条件叫 \(m\ge n-2\),我们不妨从此入手解决这道题. 我们先来探究 \ ...
- Codeforces 526G - Spiders Evil Plan(长链剖分+直径+找性质)
Codeforces 题目传送门 & 洛谷题目传送门 %%%%% 这题也太神了吧 storz 57072 %%%%% 首先容易注意到我们选择的这 \(y\) 条路径的端点一定是叶子节点,否则我 ...
随机推荐
- Golang通脉之函数
函数是组织好的.可重复使用的.用于执行指定任务的代码块. Go语言中支持函数.匿名函数和闭包,并且函数在Go语言中属于"一等公民". 函数定义 Go语言中定义函数使用func关键字 ...
- 【原创】Linux v4l2框架分析
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- JuiceFS 如何帮助趣头条超大规模 HDFS 降负载
作者简介 王振华,趣头条大数据总监,趣头条大数据负责人. 王海胜,趣头条大数据工程师,10 年互联网工作经验,曾在 eBay.唯品会等公司从事大数据开发相关工作,有丰富的大数据落地经验. 高昌健,Ju ...
- Vue el 使用el-checkbox-group复选框进行单选框操作
el-checkbox-group这个组件与其他复选框不一样,我当初也是半天不知道怎么操作 页面使用v-model绑定 size就是等比例缩小放大,v-ror循环应该看的懂.重要的是@chage到我们 ...
- WiFi模块选型参考
经常会碰到一些关于wifi模块的咨询,很多刚接触wifi模块的设计人员或者用户,只知道提wifi模块,很难提具体的模块要求!希望通过文章的介绍,会做到有的放矢!咨询时一定要搞清楚自己希望使用什么主芯片 ...
- 硬件工程师必须掌握的PCB叠层设计内容
总的来说叠层设计主要要遵从两个规矩: 1. 每个走线层都必须有一个邻近的参考层(电源或地层); 2. 邻近的主电源层和地层要保持最小间距,以提供较大的耦合电容; 下面列出从两层板到八层板的叠层来进行示 ...
- 【代码更新】单细胞分析实录(20): 将多个样本的CNV定位到染色体臂,并画热图
之前写过三篇和CNV相关的帖子,如果你做肿瘤单细胞转录组,大概率看过: 单细胞分析实录(11): inferCNV的基本用法 单细胞分析实录(12): 如何推断肿瘤细胞 单细胞分析实录(13): in ...
- Python hashlib Unicode-objects must be encoded before hashing
Python2中没有这个问题 python3中 hashlib.md5(data)函数中data 参数的类型应该是bytes hash前必须把数据转换成bytes类型 Python 2.7.12 (d ...
- 便宜的回文串(区间DP)
题目链接:便宜的回文串 这道题刚开始其实还是没有思路的.没办法,只能看题解了... 其实我们在思考问题时,考虑到一段串增或减时会改变它的长度,所以转移时会麻烦... 但其实不用考虑那么多的问题,我们只 ...
- Luogu P2827 [NOIp2016提高组]蚯蚓 | 神奇的队列
题目链接 80分思路: 弄一个优先队列,不停地模拟,切蚯蚓时就将最长的那一条出队,然后一分为二入队,简单模拟即可.还要弄一个标记,表示从开始到当前时间每一条蚯蚓应该加上的长度,操作时就加上,入队时就减 ...