Codeforces 题目传送门 & 洛谷题目传送门

qwq 这题大约是二十来天前 AC 的罢,为何拖到此时才完成这篇题解,由此可见我是个名副其实的大鸽子(

这是我上 M 的那场我没切掉的 F2 哦,u1s1 我上 M 还要多亏那场的 F1 啊(

首先暴力 \(3^nn\) 枚举子集显然是过不去的,否则这题就退化到 F1 了(大雾

考虑用一个容斥的思想,考虑求出答案的后缀和 \(f_i\),其中 \(i\) 是一个长度为 \(n-1\) 的二进制串。形式化地说,\(f_i\) 等于满足以下条件的排列 \(p\) 的个数:如果 \(i\) 的第 \(j\) 位为 \(1\) 那么 \(p_j,p_{j+1}\) 必须认识,否则 \(p_j,p_{j+1}\) 可以认识可以不认识。显然求出 \(f_i\) 之后再做一遍高维差分——或者你叫它 IFWTand 我也没意见,即可求出真正的 \(ans_i\)。

接下来考虑怎样求出 \(f_i\),首先我们将 \(i\) 用二进制表示,显然 \(i\) 的二进制表示可以划分成若干个连续的 \(1\) 组成的段,假设每一段的长度分别为 \(l_1,l_2,\cdots,l_m\),那么这个划分方案数就等价于将全集 \(U=\{1,2,\cdots,n\}\) 划分为 \(m\) 个集合 \(S_1,S_2,\cdots,S_m\),满足 \(|S_i|=l_i\),再将 \(S_i\) 中的所有人排成一列,满足相邻的人必须互相认识的方案数。注意到题目中涉及”对于集合 \(S\),将 \(S\) 中的人排成一列,满足相邻的人必须互相认识的方案数 \(g_S\)“的子问题,这个是非常容易求得的,简单设个 \(dp_{S,i}\) 表示将 \(S\) 中的人排成一列,最后一个人为 \(i\) 并且相邻的人互相认识的方案数,这样即可在 \(2^nn^2\) 的时间内求出 \(g_S\),这样一来 \(f_i=\sum\limits_{|S_i|=l_i,S_1\cup S_2\cdots\cup S_m=U}\prod\limits_{i=1}^mg_{S_i}\),这个看起来有点像子集卷积,我们再将原来的一维数组 \(g_S\) 扩充成二维数组 \(h_{i,S}\),\(h_{i,S}=\begin{cases}g_S&(|S|=i)\\0&(|S|\ne i)\end{cases}\),再将所有 \(h_{l_i}\) FWTor 卷起来得到集合幂级数 \(F\),最终的 \(F_U\) 即为 \(f_i\)。

有人可能会说,你对每个 \(f_i\) 都搞一遍 FWTor,这样不就变成 \(4^nn\) 了吗?甚至跑不过 \(3^nn\) 的暴力。事实上注意到段与段之间的顺序是不重要的,而 \(18\) 的整数划分总共只有 \(385\) 种,因此我们可以对每个整数划分都求一遍答案,求 \(f_i\) 时直接调用刚刚求得的值即可。

时间复杂度大约是 \(\mathcal O(385·2^n+2^nn^2)\)

const int MAXP=1<<18;
const int MAXN=18;
int n;ll dp[MAXP+5][MAXN+5],g[MAXN+5][MAXP+5];
char s[MAXN+5][MAXN+5];
void FWTor(ll *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++)
a[(i>>1)+j+k]+=a[j+k]*type;
}
void FWTand(ll *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++)
a[j+k]+=a[(i>>1)+j+k]*type;
}
map<vector<int>,ll> ret;
vector<int> cur;
ll mul[MAXN+5][MAXP+5],ans[MAXP+5];
void dfs(int x,int lst){
if(x==n+1){
ll sum=0;
for(int i=0;i<(1<<n);i++){
int dig=bitcnt(((1<<n)-1)^i)&1;
if(dig) sum-=mul[cur.size()][i];
else sum+=mul[cur.size()][i];
} ret[cur]=sum;
// for(int x:cur) printf("%d ",x);printf("%lld\n",sum);
return;
}
for(int i=lst;i<=n-x+1;i++){
cur.pb(i);
for(int j=0;j<(1<<n);j++) mul[cur.size()][j]=mul[cur.size()-1][j]*g[i][j];
dfs(x+i,i);cur.pop_back();
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
for(int i=1;i<=n;i++) dp[1<<i-1][i]=1;
for(int i=1;i<(1<<n);i++) for(int j=1;j<=n;j++) if(i>>(j-1)&1){
for(int k=1;k<=n;k++) if((i>>(k-1)&1)&&j!=k&&s[k][j]=='1'){
dp[i][j]+=dp[i^(1<<j-1)][k];
} g[bitcnt(i)][i]+=dp[i][j];
}
// for(int i=1;i<(1<<n);i++) printf("%lld\n",g[bitcnt(i)][i]);
for(int i=1;i<=n;i++) FWTor(g[i],1<<n,1);
for(int i=0;i<(1<<n);i++) mul[0][i]=1;dfs(1,1);
for(int i=0;i<(1<<n-1);i++){
vector<int> v;int pre=-1;
for(int j=0;j<n-1;j++) if(~i>>j&1){
v.pb(j-pre);pre=j;
} v.pb(n-1-pre);sort(v.begin(),v.end());
// for(int x:v) printf("%d ",x);printf("\n");
ans[i]=ret[v];
} FWTand(ans,1<<n-1,-1);
for(int i=0;i<(1<<n-1);i++) printf("%lld ",ans[i]);
return 0;
}

Codeforces 1326F2 - Wise Men (Hard Version)(FWT+整数划分)的更多相关文章

  1. CodeForces 768E SG函数 整数划分 Game of Stones

    一个标准的NIM游戏 加上一条规则:每堆石子对于每个数目的石子只能被取一次 可以SG打表 dp[i][j]表示现在有i个石子 j是可以取的石子数的状压 第i位为1就表示i个石子没被取过 #includ ...

  2. 51nod p1201 整数划分

    1201 整数划分 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 将N分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} {1,5} {2, ...

  3. 2014北大研究生推免机试(校内)-复杂的整数划分(DP进阶)

    这是一道典型的整数划分题目,适合正在研究动态规划的同学练练手,但是和上一个随笔一样,我是在Coursera中评测通过的,没有找到适合的OJ有这一道题(找到的ACMer拜托告诉一声~),这道题考察得较全 ...

  4. 整数划分 (区间DP)

    整数划分(四) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 暑假来了,hrdv 又要留学校在参加ACM集训了,集训的生活非常Happy(ps:你懂得),可是他最近 ...

  5. nyoj 90 整数划分

    点击打开链接 整数划分 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 将正整数n表示成一系列正整数之和:n=n1+n2+-+nk,  其中n1≥n2≥-≥nk≥1,k≥ ...

  6. 整数划分 Integer Partition(二)

    本文是整数划分的第二节,主要介绍整数划分的一些性质. 一 先来弥补一下上一篇文章的遗留问题:要求我们所取的 (n=m1+m2+...+mi )中  m1 m2 ... mi连续,比如5=1+4就不符合 ...

  7. 整数划分 Integer Partition(一)

    话说今天百度面试,可能是由于我表现的不太好,面试官显得有点不耐烦,说话的语气也很具有嘲讽的意思,搞得我有点不爽.Whatever,面试中有问到整数划分问题,回答这个问题过程中被面试官搞的不胜其烦,最后 ...

  8. 51nod1201 整数划分

    01背包显然超时.然后就是一道神dp了.dp[i][j]表示j个数组成i的方案数.O(nsqrt(n)) #include<cstdio> #include<cstring> ...

  9. NYOJ-571 整数划分(三)

    此题是个非常经典的题目,这个题目包含了整数划分(一)和整数划分(二)的所有情形,而且还增加了其它的情形,主要是用递归或者说是递推式来解,只要找到了递推式剩下的任务就是找边界条件了,我觉得边界也是非常重 ...

随机推荐

  1. WIFI Deauth攻击-爬坑笔记

    这里用Aircrack这款工具进行介绍: 准备工作:无线网卡连接电脑或者虚拟机(免驱的最好),如需驱动请自行安装驱动 1.将无线网卡接入测试电脑Linux虚拟机(装有Aircrack-ng) 2.测试 ...

  2. 编程题:X星人的金币

    X星人的金币 时问限制:3000MS 内存限制:589824KB 题目描述: X是人在一艘海底沉船上发现了很多很多很多金币.可爱的X星人决定用这些金币来玩一个填格子的游戏.其规则如下:第1个格子放2枚 ...

  3. SpringCloud微服务实战——搭建企业级开发框架(七):自定义通用响应消息及统一异常处理

      平时开发过程中,无可避免我们需要处理各类异常,所以这里我们在公共模块中自定义统一异常,Spring Boot 提供 @RestControllerAdvice 注解统一异常处理,我们在GitEgg ...

  4. 【二食堂】Beta - Scrum Meeting 6

    Scrum Meeting 6 例会时间:5.19 18:30~18:50 进度情况 组员 当前进度 今日任务 李健 1. 实体标注的优化基本已经实现,后端有bug,还没有进行接口调用 issue 2 ...

  5. 无网络下,配置yum本地源

    1. 新建一个没有iso镜像文件的虚拟机: 2. 本地上传一个镜像文件(CentOS7的镜像),到虚拟机已创建的目录: 例如:上传一个镜像文件CentOS-7-x86_64-Everything-17 ...

  6. Noip模拟17 2021.7.16

    我愿称这场考试为STL专练 T1 世界线 巧妙使用$bitset$当作vis数组使用,内存不会炸,操作还方便,的确是极好的. 但是这个题如果不开一半的$bitset$是会炸内存的,因为他能开得很大,但 ...

  7. 8.18考试总结[NOIP模拟43]

    又挂了$80$ 好气哦,但要保持优雅.(草 T1 地衣体 小小的贪心:每次肯定从深度较小的点向深度较大的点转移更优. 模拟一下,把边按链接点的子树最大深度排序,发现实际上只有上一个遍历到的点是对当前考 ...

  8. 零基础入门必备的Linux命令和C语言基础

    文件和目录(底部有视频资料) cd /home 进入 '/ home' 目录' cd - 返回上一级目录 cd -/- 返回上两级目录 cd 进入个人的主目录 cd ~user1 进入个人的主目录 c ...

  9. iPhone SE切换颜色特效

    Apple 网站的特效, iPhone SE 共有黑.白.红三种颜色,在卷动页面的时候会逐步替换,看起来效果非常时尚,在此供上代码学习. <!DOCTYPE html> <html& ...

  10. 在Ubuntu下安装Solr

    使用wget命令去官网下载solr的压缩包. 1 wget https://mirrors.bfsu.edu.cn/apache/lucene/solr/8.6.3/solr-8.6.3.tgz 使用 ...