CF1326F2 Wise Men (Hard Version)
CF1326F2 Wise Men (Hard Version)
题解
神题,看了一天才看懂,主要是 \(FWT\) 忘了怎么做,导致有些地方想了好久,但实际上很多地方都可以不用 \(FWT\)。
首先我们可以将 \(01\) 串看做一堆链拼在一起,链与链之间是否有连接会对 \(01\) 作影响。
考虑用容斥去掉链与链之间的影响,\(01\) 串中为 1 表示必须为 \(1\),否则没有限制。
这样我们就可以将 \(01\) 串看做一堆链拼在一起,并且不用考虑链与链之间的影响了。
容易发现这样答案变成了原来的超集和,超集和实际上就是 \(FWT\) 的与变换,到最后用 \(IFWT\) 推回去就行了。
注意到一个事情,由于链与链之间的影响已经被消去了,所以链之间的顺序也就没有关系了,也就是说本质相同的字符串答案相同。
$eg:(1100111)=(0110111)= [1,3,4] $
字符串给的是差的形式,本质不同要将其转化为连的形式,容易发现本质不同的个数其实是 \(n\) 的划分数,注意到最大的划分数 \(P(n)\) 最大只有 \(385\)。
考虑已知划分数的情况如何求取容斥后的答案,显然有这样一个东西:
\(s_i\) 表示第 \(i\) 个划分数所用的点集,\(g_{s_i,|s_i|}\) 表示从点集 \(s_i\) 中选择一条长度为 \(|s_i|\) 的链的方案数,所用点集显然要满足以下条件:
这东西显然长得很像子集卷积,考虑一边爆搜一遍维护这个东西。
注意到一个性质,所有 \(s_i\) 的并都是 \(U\) ,所以只有交为空的点集会对答案作贡献,所以可以不用子集卷积,用 \(FWT_{or}\)
就行了。
先对于每一个不同长度的 \(g\) 数组做一遍 \(FWT_{or}\),然后在爆搜的时候直接点乘。
到最后我们要求出 \(g_U\) 的值,注意到我们只需要这一个地方的值,所以我们不需要每次都 \(IFWT\) ,直接根据定义往下推就好了。
最后将划分数哈希之后,对于每一个 \(01\) 串找到其对应的状态就好了。
\(g\) 数组很好求,用 \(f_{i,j}\) 表示所用点集为 \(i\) ,结尾为 \(j\) 的方案数,\(g\) 数组就是 \(f\) 的一个累和。
时间复杂度 \(O(S(n)2^n)\),\(S(n)\) 为划分树的搜索树大小。
点击查看代码
// LUOGU_RID: 121469744
#include <bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define rg register
#define pc putchar
#define gc getchar
#define pf printf
#define space pc(' ')
#define enter pc('\n')
#define me(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define FOR(i,k,t,p) for(rg int i(k) ; i <= t ; i += p)
#define ROF(i,k,t,p) for(rg int i(k) ; i >= t ; i -= p)
using namespace std ;
bool s_gnd ;
inline void read(){}
template<typename T,typename ...T_>
inline void read(T &x,T_&...p)
{
x = 0 ;rg int f(0) ; rg char c(gc()) ;
while(!isdigit(c)) f |= (c=='-'),c = gc() ;
while(isdigit(c)) x = (x<<1)+(x<<3)+(c^48),c = gc() ;
x = (f?-x:x) ;
read(p...);
}
int stk[30],tp ;
inline void print(){}
template<typename T,typename ...T_>
inline void print(T x,T_...p)
{
if(x < 0) pc('-'),x = -x ;
do stk[++tp] = x%10,x /= 10 ; while(x) ;
while(tp) pc(stk[tp--]^48) ; space ;
print(p...) ;
}
bool S_GND ;
const int N = 19 ;
int n,T ; ull base = 1e5+7 ;
int st[N],f[N][1<<N],g[N][1<<N],now[N][1<<N],ans[1<<N] ;
char c[N][N] ; unordered_map<ull,int>num ;
void FWT_or(int *p)
{
for(int k = 1,o = 2 ; o <= T+1 ; o <<= 1,k <<= 1)
FOR(i,0,T,o) FOR(j,0,k-1,1) p[i+j+k] += p[i+j] ;
}
void IFWT_and(int *p)
{
for(int k = 1,o = 2 ; o <= T+1 ; o <<= 1,k <<= 1)
FOR(i,0,T,o) FOR(j,0,k-1,1) p[i+j] -= p[i+j+k] ;
}
void Dfs(int x,int las,int sum,ull hh)
{
if(sum == n+1)
{
FOR(S,0,T,1)
num[hh] += now[x][S]*((__builtin_popcount(T^S)&1)?-1:1) ;
return ;
}
FOR(i,las,n-sum+1,1)
{
FOR(S,0,T,1) now[x+1][S] = now[x][S]*g[i][S] ;
Dfs(x+1,i,sum+i,hh*base+i) ;
}
}
signed main()
{
//cerr<<(double)(&s_gnd-&S_GND)/1024.0/1024.0 ;
// freopen(".in","r",stdin) ;
// freopen(".out","w",stdout) ;
read(n),T = (1<<n)-1,--n ;
FOR(i,0,n,1) scanf("%s",c[i]) ;
FOR(i,0,n,1) f[i][1<<i] = 1 ;
FOR(S,0,T,1) FOR(i,0,n,1)
{
if(!f[i][S] || !((S>>i)&1)) continue ;
FOR(j,0,n,1) if(!((S>>j)&1) && c[i][j] == '1')
f[j][S|(1<<j)] += f[i][S] ;
}
FOR(S,0,T,1) FOR(i,0,n,1) g[__builtin_popcount(S)][S] += f[i][S] ;
FOR(i,0,n+1,1) FWT_or(g[i]) ;
FOR(S,0,T,1) now[0][S] = 1 ; Dfs(0,1,0,0) ;
FOR(S,0,(1<<n)-1,1)
{
ull val = 0 ; int top = 0,ttp = 1 ;
FOR(i,0,n-1,1)
if((S>>i)&1) ++ttp ;
else st[++top] = ttp,ttp = 1 ;
st[++top] = ttp,sort(st+1,st+1+top) ;
FOR(i,1,top,1) val = val*base+st[i] ; ans[S] = num[val] ;
}
IFWT_and(ans) ; FOR(S,0,(1<<n)-1,1) print(ans[S]) ;
return 0 ;
}

浙公网安备 33010602011771号