题目链接
题目解法
挺难的。可能一步一步推下来也没那么复杂(?
基本 copy tzc_wk 的题解/bx
肯定不能像 \(F1\) 用普通的状压求,一个技巧是容斥
考虑令 \(f_S\) 表示 \(S\) 中为 \(1\) 的位置 \(p_i\) 和 \(p_{i+1}\) 必须认识,为 \(0\) 的位置随便
\(f\) 数组相当于答案的高维后缀和,若求出 \(f\),还原答案是简单的
考虑如何求出 \(f\)
我们把 \(S\) 的二进制表示拆分成若干个连续 \(1\) 段,假设长度为 \(l_1,l_2,...,l_m\)
这相当于把全集分成 \(m\) 个集合,满足 \(|S_i|=l_i\),然后把 \(S_i\) 内部钦定一个顺序,满足相邻两个都认识 的方案数
后面的部分是好做的,令 \(dp_{S,i}\) 表示当前有集合 \(S\) 中的人,最后一个人是 \(i\),且相邻人都认识的方案数,转移是 simple 的
前面的部分比较有启发性
令 \(g_S\) 表示 \(S\) 集合内任意排序,相邻人都认识的方案数
那么总方案数就是 \(\prod \limits_{|S_i|=l_i,S_1|S_2|...|S_m=全集} g_{S_i}\)
这东西看上去类似子集卷积
我们在记一维 \(popcount\),然后对每个 \(popcount\) 做一遍 \(fwt\)
然后再把 \(l_i\) 对应的点值序列都对应位相乘,最后 \(ifwt\) 还原回去即可
注意到我们只需要全集的值,所以这一部分并不需要显式的 \(ifwt\),直接容斥即可
但这样做复杂度是 \(O(4^nn)\),显然不太行
但我们发现 \(l_i\) 的顺序是不影响答案的,所以我们只求 \(l_i\) 不降的方案数即可
不难发现这相当于枚举 \(n\) 的拆分数,而 \(18\) 的拆分数个数只有 \(385\)
所以最后的时间复杂度为 \(O(385\times 2^n+2^nn^2)\)
#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
FF=0;int RR=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
FF*=RR;
}
typedef vector<int> vi;
const int N=18;
int n,ppc[1<<N];
LL ans[1<<N],rec[N+1][1<<N],f[1<<N][N];
bool E[N][N];
int all(int x){ return (1<<x)-1;}
void fwtor(LL *val){ F(i,0,n-1) F(S,0,(1<<n)-1) if(S>>i&1) val[S]+=val[S^(1<<i)];}
map<vi,LL> mp;
vi dvd;
LL mul[N+1][1<<N];
void doit(){
LL res=0;
F(S,0,all(n)){
if((n-ppc[S])&1) res-=mul[dvd.size()][S];
else res+=mul[dvd.size()][S];
}
mp[dvd]=res;
}
void dfs(int lef,int dep){
if(!lef){ doit();return;}
F(i,dep,lef){
dvd.pb(i);
F(S,0,(1<<n)-1) mul[dvd.size()][S]=mul[SZ(dvd)][S]*rec[i][S];
dfs(lef-i,i);
dvd.pop_back();
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
cin>>n;
F(i,0,n-1){
string str;cin>>str;
F(j,0,n-1) E[i][j]=str[j]-48;
}
F(S,0,all(n)) ppc[S]=__builtin_popcount(S);
F(i,0,n-1) f[1<<i][i]=1;
F(S,1,all(n)) F(i,0,n-1) if(f[S][i]){
rec[ppc[S]][S]+=f[S][i];
F(j,0,n-1) if(!(S>>j&1)&&E[i][j]) f[S^1<<j][j]+=f[S][i];
}
F(i,1,n) fwtor(rec[i]);
F(i,0,all(n)) mul[0][i]=1;
dfs(n,1);
F(S,0,all(n-1)){
dvd.clear();
F(i,0,n-1){
int j=i;
while(j<n-1&&(S>>j&1)) j++;
dvd.pb(j-i+1),i=j;
}
sort(dvd.begin(),dvd.end());
ans[S]=mp[dvd];
}
F(i,0,n-2) F(S,0,all(n-1)) if(S>>i&1) ans[S^(1<<i)]-=ans[S];
F(i,0,all(n-1)) printf("%lld ",ans[i]);puts("");
return 0;
}
标签:ch,Wise,int,题解,Men,long,FF,dvd,define
From: https://www.cnblogs.com/Farmer-djx/p/18377638