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. .jar文件没有Java(TM) Platform SE binary打开方式解决办法

    下面是我个人在打开.jar文件时候的一些小问题: 明明已经配置好了环境变量.jar文件却没有 Java(TM) Platform SE binary 的打开方式, 网上查了资料点明是环境变量的问题,后 ...

  2. Mybatis 二级缓存应用 (21)

    [MyBatis 二级缓存] 概述:一级缓存作用域为同一个SqlSession对象,而二级缓存用来解决一级缓存不能夸会话共享,作用范围是namespace级,可以被多个SqlSession共享(只要是 ...

  3. Hadoop MapReduce 保姆级吐血宝典,学习与面试必读此文!

    Hadoop 涉及的知识点如下图所示,本文将逐一讲解: 本文档参考了关于 Hadoop 的官网及其他众多资料整理而成,为了整洁的排版及舒适的阅读,对于模糊不清晰的图片及黑白图片进行重新绘制成了高清彩图 ...

  4. Coursera Deep Learning笔记 结构化机器学习项目 (上)

    参考:https://blog.csdn.net/red_stone1/article/details/78519599 1. 正交化(Orthogonalization) 机器学习中有许多参数.超参 ...

  5. logstash multi pipeline的使用

    logstash multi pipeline的使用 一.背景 二.解决方案 1.方案一: 2.方案二: 3.方案三: 三.实现步骤 1.编写 pipeline 文件 1.从文件收集,输出到控制台 2 ...

  6. NGINX杂谈——flask_limiter的IP获取(怎么拿到真实的客户端IP)

    本篇博客将 flask_limiter 作为切入点,来记录一下自己对 remote_addr 和 proxy_add_x_forwarded_for 两个变量.X-Real-IP 和 X-Forwar ...

  7. Noip模拟71 2021.10.7

    T1 签到题 结论题,找到规律就会做 规律是每个点的度数$\mod$颜色种数,如果不是$0$则贡献一个答案 1 #include<bits/stdc++.h> 2 #define int ...

  8. Java-基础-LinkedList

    1. 简介 LinkedList 同时实现了List和Deque接口,也就是说它既可以看作是一个顺序容器,又可以看作是双向队列. 既然是双向列表,那么它的每个数据节点都一定有两个指针,分别指向它的前驱 ...

  9. STM32时钟系统之利用 systick 定时器来实现准确的延时。

    本篇文章带着大家来认识一下 STM32 的时钟系统,以及利用 systick 定时器来实现一个比较准确的延时. 我们首先从时钟说起,时钟在MCU中的作用,就好比于人类的心脏一样不可或缺.STM32 的 ...

  10. Qt char * 转QString

    1.QString转char * 先将QString转换为QByteArray,再将QByteArray转换为char *. 注意:不能用char *mm = str.toLatin1().data( ...