洛谷 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\)位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 \( ... 
随机推荐
- 盘点一下Redis中常用的Java客户端,或者咱们手写一个?
			Java中那些Redis的客户端 前面我们的所有操作都是基于redis-cli来完成的,那么我们要在Java中操作Redis,怎么做呢?首先我们先来了解一下Redis Serialization Pr ... 
- Java:锁笔记
			Java:锁笔记 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 1. Java 锁之公平锁和非公平锁 公平锁 是指多个线程按照申请锁的顺序来获取锁,类似于排队买 ... 
- the Agiles Scrum Meeting 9
			会议时间:2020.4.17 20:00 1.每个人的工作 今天已完成的工作 个人结对项目增量开发组:基本实现个人项目创建.仓库自动分配,修复bug issues: 增量组:准备评测机制,增加仓库自动 ... 
- [源码解析] Pytorch 如何实现后向传播 (2)---- 引擎静态结构
			[源码解析] Pytorch 如何实现后向传播 (2)---- 引擎静态结构 目录 [源码解析] Pytorch 如何实现后向传播 (2)---- 引擎静态结构 0x00 摘要 0x01 Engine ... 
- spring security实现简单的url权限拦截
			在一个系统中,权限的拦截是很常见的事情,通常情况下我们都是基于url进行拦截.那么在spring security中应该怎么配置呢. 大致步骤如下: 1.用户登录成功后我们需要拿到用户所拥有的权限,并 ... 
- 极简实用的Asp.NetCore框架再新增商城模块
			概述 关于这个框架的背景,在前面我已经交代过了.不清楚的可以查看这个链接 1.极简实用的Asp.NetCore模块化框架决定免费开源了 2.极简实用的Asp.NetCore模块化框架新增CMS模块 算 ... 
- Noip模拟40 2021.8.15
			T1 送花 按照题解意思说是扫描线题,但我觉得像一个线段树优化$dp$ 主要思想一样,就是暴力枚举右端点,同时维护左端点的最值, 考虑两种情况, 如果左端点在$r$扫到的数$i$上一次出现的位置之前, ... 
- [BZOI2014]大融合——————线段树进阶
			竟然改了不到一小时就改出来了, 可喜可贺 Description Solution 一开始想的是边两侧简单路径之和的乘积,之后发现这是个树形结构,简单路径数就是节点数. 之后的难点就变成了如何求线段树 ... 
- 洛谷 P4555 [国家集训队]最长双回文串
			链接: P4555 题意: 在字符串 \(S\) 中找出两个相邻非空回文串,并使它们长度之和最大. 分析: 直接使用马拉车算法求出每个点扩展的回文串.如果枚举两个回文串显然会超时,我们考虑切割一个长串 ... 
- 注意 .NET string.GetHashCode() 用法
			需求案例:需要把字符串存入数据库,并且要求数据库中不能有重复的字符串,由此就引出了将字符串hash成特定的hash值,依靠查询hash值是否重复来判断字符串是否重复.这样做的好处在于查询重复字符串的代 ... 
