Codeforces 1326F2 - Wise Men (Hard Version)(FWT+整数划分)
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+整数划分)的更多相关文章
- CodeForces 768E SG函数 整数划分 Game of Stones
一个标准的NIM游戏 加上一条规则:每堆石子对于每个数目的石子只能被取一次 可以SG打表 dp[i][j]表示现在有i个石子 j是可以取的石子数的状压 第i位为1就表示i个石子没被取过 #includ ...
- 51nod p1201 整数划分
1201 整数划分 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 将N分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} {1,5} {2, ...
- 2014北大研究生推免机试(校内)-复杂的整数划分(DP进阶)
这是一道典型的整数划分题目,适合正在研究动态规划的同学练练手,但是和上一个随笔一样,我是在Coursera中评测通过的,没有找到适合的OJ有这一道题(找到的ACMer拜托告诉一声~),这道题考察得较全 ...
- 整数划分 (区间DP)
整数划分(四) 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 暑假来了,hrdv 又要留学校在参加ACM集训了,集训的生活非常Happy(ps:你懂得),可是他最近 ...
- nyoj 90 整数划分
点击打开链接 整数划分 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 将正整数n表示成一系列正整数之和:n=n1+n2+-+nk, 其中n1≥n2≥-≥nk≥1,k≥ ...
- 整数划分 Integer Partition(二)
本文是整数划分的第二节,主要介绍整数划分的一些性质. 一 先来弥补一下上一篇文章的遗留问题:要求我们所取的 (n=m1+m2+...+mi )中 m1 m2 ... mi连续,比如5=1+4就不符合 ...
- 整数划分 Integer Partition(一)
话说今天百度面试,可能是由于我表现的不太好,面试官显得有点不耐烦,说话的语气也很具有嘲讽的意思,搞得我有点不爽.Whatever,面试中有问到整数划分问题,回答这个问题过程中被面试官搞的不胜其烦,最后 ...
- 51nod1201 整数划分
01背包显然超时.然后就是一道神dp了.dp[i][j]表示j个数组成i的方案数.O(nsqrt(n)) #include<cstdio> #include<cstring> ...
- NYOJ-571 整数划分(三)
此题是个非常经典的题目,这个题目包含了整数划分(一)和整数划分(二)的所有情形,而且还增加了其它的情形,主要是用递归或者说是递推式来解,只要找到了递推式剩下的任务就是找边界条件了,我觉得边界也是非常重 ...
随机推荐
- C++/CLR 使用(VS2012,VS2013,VS2015)编写
转载自:http://www.th7.cn/system/win/201509/129417.shtml VS2010以及以前的版本,创建项目时都可以在CLR下找到"Windows窗体应用程 ...
- 更好的 java 重试框架 sisyphus 入门简介
What is Sisyphus sisyphus 综合了 spring-retry 和 gauva-retrying 的优势,使用起来也非常灵活. 为什么选择这个名字 我觉得重试做的事情和西西弗斯很 ...
- vue3.x相对于vue2.x生命周期改动
vue3.x已经正式发布了,部分小伙伴已经用了vue3.x开发,部分小伙伴还在观望中,下面是两个影响比较大的改动 1.beforeDestroy和destroyed不能用了. 这个应该是vue2.x项 ...
- Go语言核心36讲(Go语言进阶技术七)--学习笔记
13 | 结构体及其方法的使用法门 我们都知道,结构体类型表示的是实实在在的数据结构.一个结构体类型可以包含若干个字段,每个字段通常都需要有确切的名字和类型. 前导内容:结构体类型基础知识 当然了,结 ...
- pycharm 服务器连接及一些问题解决
主要介绍一下如何使用pycharm连接服务器并在服务器上炼丹,并对遇到的一个小问题进行说明. 目录 1,SSH连接 2,linux常用命令 3,配置anaconda 4,运行代码 5,一个常见错误 1 ...
- 实验 1: SDN拓扑实践
(图片和文档是自己写的,因为在CSDN也写了,所以会有自己的水印) 一.实验目的 能够使用源码安装Mininet: 能够使用Mininet的可视化工具生成拓扑: 能够使用Mininet的命令行生成特定 ...
- Java 将Excel转为et和ett格式
以.et结尾的文件格式是属于金山办公软件WPS Office中的电子表格文件,.ett是一种模板文件格式.除了通过WPS软件可以创建该格式的电子表格外,也可以通过格式转换的方法来获得,如将Micros ...
- advanced base-scripting guide in chinese(高级Bash脚本编程指南-10)
<高级Bash脚本编程指南>Revision 10中文版 github上链接地址: https://github.com/LinuxStory/Advanced-Bash-Scriptin ...
- 数组中的逆序对 牛客网 剑指Offer
数组中的逆序对 牛客网 剑指Offer 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对10000000 ...
- 寻找下一个结点 牛客网 程序员面试金典 C++ java Python
寻找下一个结点 牛客网 程序员面试金典 C++ java Python 题目描述 请设计一个算法,寻找二叉树中指定结点的下一个结点(即中序遍历的后继). 给定树的根结点指针TreeNode* root ...