洛谷 P6570 - [NOI Online #3 提高组] 优秀子序列(集合幂级数+多项式)
首先 \(3^n\) 的做法就不多说了,相信对于会状压 dp+会枚举子集的同学来说不算困难(暴论),因此这篇博客将着重讲解 \(2^nn^2\) 的做法。
首先如果我们把每个 \(a_i\) 看作一个集合幂级数 \(1+x^{a_i}\),那么我们的任务就是把所有这样的集合幂级数做一遍子集卷积对吧。直接做一脸过不去。不过注意到这个式子的形式比较特别,事实上学过多项式&生成函数的同学应该对形如 \(1+x^k\) 的式子特别敏感,因为在生成函数那套理论中有个恒等式 \(\ln(1+x^k)=\sum\limits_{i}(-1)^{i+1}\dfrac{x^{ik}}{i}\),因此考虑将这东西与多项式扯上关系。考虑子集卷积的本质:将所有 \(x^{S}\) 看作一个二维函数 \(x^{S}y^{|S|}\),然后对 \(x\) 的指数做 or 卷积,对 \(y\) 的指数做加法卷积。那么我们考虑做这样一件事:把所有集合幂级数 \(F(S)\) 写成 \(\sum\limits_{S}F_{S}(y)x^S\) 的形式,也就是外层是集合幂级数,内层是一个关于 \(y\) 的多项式。那么考虑两个幂级数 \(F(S)\) 和 \(G(S)\) 做子集卷积得到的幂级数 \(H(S)\),必然有 \(H_i(y)=\sum\limits_{j|k=i}F_j(y)G_k(y)\),因此在这种定义下,对两个幂级数进行子集卷积的过程即是:将 \(F_{S}(y)x^S\) 做一遍 FWTor,也就是把平时对整数的 FWTor 的加法改为多项式加法,对 \(G_S(y)x^S\) 也做一遍同样的操作,然后令 \(H_S(y)=F_S(y)G_S(y)\),也就是将 FWT 后对应位置上的多项式卷起来,然后再 IFWTor 回去即可。
直接照着上面的方式做还是会 TLE,不过注意到将集合幂级数 \(1+x^{a_i}\) 进行一遍 FWTor 后得到的集合幂级数比较特别,具体来说,\(1=x^0y^0\) 显然可以对 FWTor 后所有位置上的数产生贡献,因此所有位置上的 \(F_S(y)\) 都有一个 \(y^0\),而 \(x^{a_i}=x^{a_i}y^{|a_i|}\) 显然只能对 \(a_i\in S\) 的 \(S\) 产生贡献,因此对于所有 \(a_i\in S\) 的 \(S\) 有 \(F_S(y)=y^{|a_i|}+1\),其余 \(S\) 有 \(F_{S}(y)=1\)。
它 出 现 了!\(y^{a_i}+1\) 显然与前面 \(1+x^k\) 是同一形式的,因此它的 \(\ln\) 我们也是可以非常轻松求得的,而我们在 FWTor 之后,按照套路是要把对应位置上的多项式全部卷起来的,按照多项式的套路我们可以先取 \(\ln\) 再 \(\exp\) 回去,看,你要的 \(\ln(y^{a_i}+1)\) 不就来了吗?我们记 \(H(S)\) 为将所有集合幂级数卷起来后得到的集合幂级数,那么对于所有 \(S\),\(\ln(\text{FWT}(H_S(y)))\) 是很好求的,具体步骤是:我们先记 \(F_{i,S}(y)\) 为一个幂级数,满足对于 \(S=a_i\),\(F_{i,S}(y)=\ln(y^{|a_i|}+1)\),其余 \(F_{i,S}=0\),对这东西对应位置上的多项式求个和,然后跑遍高维前缀和(或者你爱叫它 FWTor 我也没意见)即可,求完 \(\ln(H_S(y))\) 以后 \(\exp\) 回去即可得到 \(\text{FWT}(H_S(y))\),然后再一波 IFWTor 即可得到真正的系数,由于多项式的长度最多只有 \(18\),因此 \(\exp\) 不用任意模数 NTT,直接暴力求即可。
const int MAXN=1<<18;
const int LOG_V=18;
const int MOD=1e9+7;
int pr[MAXN/6+5],prcnt=0,vis[MAXN+5],phi[MAXN+5];
void sieve(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) pr[++prcnt]=i,phi[i]=i-1;
for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
vis[pr[j]*i]=1;
if(i%pr[j]==0){phi[i*pr[j]]=phi[i]*pr[j];break;}
phi[i*pr[j]]=phi[i]*phi[pr[j]];
}
}
}
int n,a[MAXN+5],f[MAXN+5][LOG_V+2],tmp[LOG_V+2],inv[LOG_V+2];
int main(){
scanf("%d",&n);sieve(MAXN);
for(int i=(inv[0]=inv[1]=1)+1;i<=LOG_V;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1,x;i<=n;i++) scanf("%d",&x),a[x]++;
for(int i=1;i<MAXN;i++){
int cnt=__builtin_popcount(i);
for(int j=1;j<=LOG_V/cnt;j++){
if(j&1) f[i][j*cnt]=(f[i][j*cnt]+1ll*inv[j]*a[i])%MOD;
else f[i][j*cnt]=(f[i][j*cnt]-1ll*inv[j]*a[i]%MOD+MOD)%MOD;
}
}
for(int i=2;i<=MAXN;i<<=1)
for(int j=0;j<MAXN;j+=i)
for(int k=0;k<(i>>1);k++)
for(int l=0;l<=LOG_V;l++){
f[(i>>1)+j+k][l]=(f[(i>>1)+j+k][l]+f[j+k][l])%MOD;
}
for(int i=0;i<MAXN;i++){
memset(tmp,0,sizeof(tmp));tmp[0]=1;
for(int j=1;j<=LOG_V;j++){
for(int k=0;k<j;k++)
tmp[j]=(tmp[j]+1ll*f[i][j-k]*(j-k)%MOD*tmp[k])%MOD;
tmp[j]=1ll*tmp[j]*inv[j]%MOD;
}
for(int j=0;j<=LOG_V;j++) f[i][j]=tmp[j];
}
for(int i=2;i<=MAXN;i<<=1)
for(int j=0;j<MAXN;j+=i)
for(int k=0;k<(i>>1);k++)
for(int l=0;l<=LOG_V;l++){
f[(i>>1)+j+k][l]=(f[(i>>1)+j+k][l]-f[j+k][l]+MOD)%MOD;
}
int ans=0;
for(int i=0;i<MAXN;i++) ans=(ans+1ll*phi[i+1]*f[i][__builtin_popcount(i)])%MOD;
for(int i=1;i<=a[0];i++) ans=2*ans%MOD;
printf("%d\n",ans);
return 0;
}
洛谷 P6570 - [NOI Online #3 提高组] 优秀子序列(集合幂级数+多项式)的更多相关文章
- luogu P6570 [NOI Online #3 提高组]优秀子序列 二进制 dp
LINK:P6570 [NOI Online #3 提高组]优秀子序列 Online 2的T3 容易很多 不过出于某种原因(时间不太够 浪了 导致我连暴力的正解都没写. 容易想到 f[i][j]表示前 ...
- 洛谷 P6478 - [NOI Online #2 提高组] 游戏(二项式反演+树形 dp)
题面传送门 没错这就是我 boom0 的那场 NOIOL 的 T3 一年前,我在 NOIOL #2 的赛场上折戟沉沙,一年后,我从倒下的地方爬起. 我成功了,我不再是从前那个我了 我们首先假设 A 拥 ...
- 洛谷P1003 铺地毯 noip2011提高组day1T1
洛谷P1003 铺地毯 noip2011提高组day1T1 洛谷原题 题目描述 为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯.一共有 n ...
- 洛谷-神奇的幻方-NOIP2015提高组复赛
题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,--,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. ...
- 洛谷 P1541 乌龟棋 & [NOIP2010提高组](dp)
传送门 解题思路 一道裸的dp. 用dp[i][j][k][kk]表示用i个1步,j个2步,k个3步,kk个4步所获得的最大价值,然后状态转移方程就要分情况讨论了(详见代码) 然后就是一开始统计一下几 ...
- 洛谷 P1525 关押罪犯 & [NOIP2010提高组](贪心,种类并查集)
传送门 解题思路 很显然,为了让最大值最小,肯定就是从大到小枚举,让他们分在两个监狱中,第一个不符合的就是答案. 怎样判断是否在一个监狱中呢? 很显然,就是用种类并查集. 种类并查集的讲解——团伙(很 ...
- 洛谷 P5019 铺设道路 & [NOIP2018提高组](贪心)
题目链接 https://www.luogu.org/problem/P5019 解题思路 一道典型的贪心题. 假设从左往右填坑,如果第i个深与第i+1个,那么第i+1个就不需要额外填: 如果第i+1 ...
- 洛谷P1063 能量项链 [2006NOIP提高组]
P1063 能量项链 题目描述 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标 记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子 ...
- 「洛谷P1080」「NOIP2012提高组」国王游戏 解题报告
P1080 国王游戏 题目描述 恰逢 \(H\)国国庆,国王邀请\(n\)位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 \( ...
随机推荐
- Less-(38~41) 堆叠注入
首先申明,Less-(38~41)可以采取和Less-(1~4)相同的解法:(一一对应) 然而,他们的漏洞其实更大,我们可以做更多具有破坏性的事情. 代码审计: Less-(38~41): 41的$s ...
- 利用 pip 安装 Python 程序包到个人用户文件夹下
利用 --user 参数,即 pip install --user package_name 这样会将Python 程序包安装到 $HOME/.local 路径下,其中包含三个字文件夹:bin,lib ...
- activemq实现队列的独有消费
在我们实际的开发中可能存在这么一种情况,应用程序要向一个队列名为queue的队列中发送3条消息,需要保证这3条消息按顺序消费.必须是第一条消费完,在消费第二条然后是第三条.而我们的程序中可能有时候存在 ...
- evaluate-reverse-polist-notation leetcode C++
Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are+,-,*, ...
- Bzoj P2212 [Poi2011]Tree Rotations | 线段树合并
题目链接 通过观察与思考,我们可以发现,交换一个结点的两棵子树,只对这两棵子树内的节点的逆序对个数有影响,对这两棵子树以外的节点是没有影响的.嗯,然后呢?(っ•̀ω•́)っ 然后,我们就可以对于每一个 ...
- oeasy教您玩转vim - 57 - # 行可视化
可视化编辑 回忆上节课内容 上次我们了解到可视模式 其实可视化对应三种子模式 字符可视模式 v 行可视模式 大写V 块可视模式ctrl+v 我们先来了解字符可视化模式 快捷键 v 可配合各种mot ...
- 小米多模网关接入Home Assistant ZNDMWG03LM
一.小米zigbee网关使用 先下载米家app,打开手机蓝牙,登陆点"我的"界面,将网关设备插上电源,橙灯闪烁,点击蓝牙网关等待弹窗提示连接,选择连接路由器(需2.4GHz),输入 ...
- 《手把手教你》系列技巧篇(四十)-java+ selenium自动化测试-JavaScript的调用执行-下篇(详解教程)
1.简介 在实际工作中,我们需要对处理的元素进行高亮显示,或者有时候为了看清楚做跟踪鼠标点击了哪些元素需要标记出来.今天宏哥就在这里把这种测试场景讲解和分享一下. 2.用法 创建一个执行 JS 的对象 ...
- CentOS8安装VirtualBox,并创建CentOS虚拟机
安装VirtualBox 执行以下命令并启用VirtualBox和EPEL包仓库 [root@localhost~] dnf config-manager --add-repo=https://dow ...
- C++ STL的一些应用
STL一些应用 记录一些STL算法在开发中用得比较舒服的情况(不断添加...) lower_bound(begin,end,val)算法 算法说明 查找>=val的第一个元素,如果没有,返回en ...