CF908H 题解
Melo.VVちゃん · · 题解
首先可以根据
然后发现显然的无解情况是对于在同一个强连通分量里的两个点,他们两个之间存在
首先原图任意点对都是弱联通的,所以如果我们已经确定了图中所有的极大强连通分量,那么边数最少的方案就是把他们随便连成一条链,同时这种构造必定合法。
考虑每个极大强连通分量,如果他的大小
容易发现在根据
现在我们只有大小超过
那么目标就是求出在合法的情况下,最少剩下几个强连通分量。
我们把强连通分量之间根据给出的
但是发现这里的子集卷积可以直接替换成或卷积,正确性显然。同时发现求的只是单个下标处的值,所以直接每次使用 FWT 数组点积乘即可,同时不需要 iFWT 回去,只需要求出每个下标对全集下标的贡献系数然后反演一遍即可,复杂度
#include "bits/stdc++.h"
#define f(i ,m ,n ,x) for (int i = (m) ,i##END = (n) ; i <= i##END ; i += (x))
const int N = 55 ;
int n ,fa[N] ,siz[N] ,a[1 << 24] ,b[1 << 24] ,v[1 << 24] ; char ch[N][N] ; bool lnk[N][N] ;
std :: vector < int > scc[N] ;
int main (void) {
std :: ios :: sync_with_stdio (false) ,
std :: cin.tie (nullptr) ,std :: cout.tie (nullptr) ;
std :: cin >> n ; f (i ,1 ,n ,1)
std :: cin >> (ch[i] + 1) ,fa[i] = i ,siz[i] = 1 ;
auto Find = [] (auto &&self ,int x) -> int
{ return x == fa[x] ? x : fa[x] = self (self ,fa[x]) ;} ;
f (i ,1 ,n - 1 ,1) f (j ,i + 1 ,n ,1) if (ch[i][j] == 'A') {
int u = Find (Find ,i) ,v = Find (Find ,j) ;
if (u == v) continue ;
else fa[u] = v ,siz[v] += siz[u] ;
}
f (i ,1 ,n - 1 ,1) f (j ,i + 1 ,n ,1) if (ch[i][j] == 'X')
if (Find (Find ,i) == Find (Find ,j)) return std :: cout << "-1\n" ,0 ;
int Single = 0 ; f (i ,1 ,n ,1)
Single += (Find (Find ,i) == i) && (siz[i] == 1) ;
int cnt = 0 ; f (i ,1 ,n ,1) if (Find (Find ,i) == i) if (siz[i] != 1)
{ cnt++ ; f (j ,1 ,n ,1) if (Find (Find ,j) == i) scc[cnt].emplace_back (j) ;}
if (!cnt) return std :: cout << Single - 1 << '\n' ,0 ;
if (cnt == 1) return std :: cout << n << '\n' ,0 ;
f (i ,1 ,cnt - 1 ,1) f (j ,i + 1 ,cnt ,1)
for (auto x : scc[i]) for (auto y : scc[j])
if (ch[x][y] == 'X') lnk[i][j] = lnk[j][i] = true ;
a[0] = 1 ; f (i ,1 ,cnt ,1) a[1 << i - 1] = 1 ;
f (j ,3 ,(1 << cnt) - 1 ,1) if (__builtin_popcount (j) > 1) {
int lbt = j & (-j) ,num = std :: __lg (lbt) + 1 ; if (a[j ^ lbt]) {
f (i ,1 ,cnt ,1) if (((j ^ lbt) >> i - 1) & 1)
if (lnk[i][num]) goto her ;
a[j] = 1 ; her : ;
}
}
[&] (int *a) -> void {
for (int len = 2 ,mid = 1 ; len <= 1 << cnt ; mid = len ,len <<= 1)
f (i ,0 ,(1 << cnt) - 1 ,len) f (j ,0 ,mid - 1 ,1) a[i + j + mid] += a[i + j] ;
} (a) ;
f (i ,0 ,(1 << cnt) - 1 ,1)
v[i] = cnt - __builtin_popcount (i) & 1 ? -1 : 1 ;
f (i ,0 ,(1 << cnt) - 1 ,1) b[i] = 1 ;
int tot = 0 ; while (true) {
tot++ ;
f (i ,0 ,(1 << cnt) - 1 ,1) b[i] *= a[i] ;
int res = 0 ; f (i ,0 ,(1 << cnt) - 1 ,1) res += v[i] * b[i] ;
if (res) return std :: cout << tot - 1 + n << '\n' ,0 ;
}
return 0 ;
}