首页 > 其他分享 >Codeforces 908H - New Year and Boolean Bridges(FWT)

Codeforces 908H - New Year and Boolean Bridges(FWT)

时间:2023-05-04 23:35:03浏览次数:40  
标签:Bridges 连通 ok 23 两点 Codeforces 908H dp 分量

一道挺有意思的题,并且感觉有点诈骗的成分在内(

首先考虑分析三种字符的性质:

  • 显然任意两点 \(i,j\) 之间要么 \(i\) 可以到达 \(j\),要么 \(j\) 可以到达 \(i\),否则 A O X 三个一个都不能满足。
  • 如果两点间的状态是 A,那么这两点必须在同一强连通分量内。
  • 如果两点间的状态是 X,那么这两点必须不能在同一强连通分量内。
  • 如果两点间的状态是 O,那么只要满足第一点的大前提,这两点就符合条件。

显然 A 描述的是一个传递闭包关系。因此先用并查集维护一波必须在同一强连通分量的点集,如果一个等价类中存在两点间的状态为 X,那么答案显然 \(-1\),先判掉这一种 trivial 的情况。

先考虑如果我们确定了强连通分量的划分情况,如何求解答案,显然对于每个 \(siz\ge 2\) 的强连通分量我们只用用 \(siz\) 条边连成一个环,而为了满足条件一的大前提,我们还需要 \(\text{强连通分量个数}-1\) 条边将它们连成一颗树,加起来发现是 \(n-1+\text{大小大于等于 2 的强连通分量个数}\),因此我们只需要最小化后者即可。

考虑如何求解这种情况。显然对 A 进行连边之后,大小为 \(1\) 的强连通分量就不用管它了,剩下 \(\ge 2\) 的强连通分量个数最多只有 \(23\) 个,刚好可以状压。考虑 X 的限制描述的什么——等价于一些强连通分量不能和别的强连通分量合并,我们先预处理 \(ok_S\) 表示 \(S\) 中的强连通分量是否能合并成一个大强连通分量——这显然有 trivial 的 \(2^{23}\) 的处理。由于答案不会很大,因此考虑最优化转计数,即设 \(dp_{i,S}\) 表示将 \(S\) 中的强连通分量划分成 \(i\) 个大强连通分量的方案数,这样有转移 \(dp_{i,S}=\sum\limits_{S_1\cup S_2=S,S_1\cap S_2=\varnothing}dp_{i-1,S_1}·ok_{S_2}\)。这样看似要子集卷积,\(2^{23}·23^2\) 似乎是似了,但是我们冷静一波,其实 \(S_1\cup S_2=\varnothing\) 这个条件是不必要的,因为如果一个集合的 \(dp_{i,S}>0\),那么其任意子集的 \(dp_{i,S}\) 也是 \(>0\) 的,因此只用异或卷积即可。对 \(ok\) 数组 FWTor,然后 \(dp_{j,2^m-1}=\sum\limits_{i=0}^{2^m-1}ok_i^j(-1)^{m-\text{popcount}(i)}\),这样复杂度少了个 \(23\),就稳了。

const int MAXN=47;
const int MAXP=1<<23;
int n,m,f[MAXN+5],siz[MAXN+5],id[MAXN+5];char s[MAXN+5][MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y){x=find(x);y=find(y);if(x^y)f[x]=y,siz[y]+=siz[x];}
int lg[MAXP+5],coef[MAXP+5],ban[MAXP+5];
ll ok0[MAXP+5],dp[MAXP+5],v[MAXP+5];
int main(){
	for(int i=1;i<=23;i++)lg[1<<i]=i;
	scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
	for(int i=1;i<=n;i++)siz[i]=1;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(s[i][j]=='A')merge(i,j);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
		if(s[i][j]=='X'&&find(i)==find(j))return puts("-1"),0;
	for(int i=1;i<=n;i++)if(siz[i]>=2&&find(i)==i)id[i]=++m;
	if(!m)return printf("%d\n",n-1),0;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(s[i][j]=='X'){
		int fi=find(i),fj=find(j);
		if(siz[fi]>=2&&siz[fj]>=2)ban[id[fi]]|=(1<<id[fj]-1);
	}
	ok0[0]=1;
	for(int i=1;i<(1<<m);i++){
		int lw=lg[i&(-i)]+1;
		ok0[i]=ok0[i&(i-1)]&((ban[lw]&i)==0);
	}
	if(ok0[(1<<m)-1])return printf("%d\n",n),0;
	for(int i=0;i<m;i++)for(int j=0;j<(1<<m);j++)if(j>>i&1)ok0[j]+=ok0[j^(1<<i)];
	for(int i=0;i<(1<<m);i++)dp[i]=ok0[i];
	for(int i=0;i<(1<<m);i++)coef[i]=((m-__builtin_popcount(i))&1)?-1:1;
	int res=n;
	while(1){
		res++;
		for(int i=0;i<(1<<m);i++)dp[i]*=ok0[i];
		ll sum=0;for(int i=0;i<(1<<m);i++)sum+=dp[i]*coef[i];
		if(sum)return printf("%lld\n",res),0;
	}
	return 0;
}
/*
4
-AXX
A-XX
XX-A
XXA-
*/

标签:Bridges,连通,ok,23,两点,Codeforces,908H,dp,分量
From: https://www.cnblogs.com/tzcwk/p/Codeforces-908H.html

相关文章

  • 4.[1201D - Treasure Hunting](https://codeforces.com/problemset/problem/1201/D)
    4.1201D-TreasureHunting题目意思:在一个n*m的地图上面,左下角的坐标是(1,1),最开始你位于左下角,一秒钟你可以进行往左或者往右的操作,你只能在一些特殊的列上面进行往上移动的操作,你不可以往下移动。现在告诉你k个宝藏的坐标信息以及哪些列是允许往上的,问最后至少要几秒可以遍历k......
  • Codeforces Round 869 (Div. 2) A-D题解
    比赛地址A.Politics题意:有n个人对m个决案进行投票,对于每一个决案如果票数相同则所有人都离场,反之票数少的一方离场,现在提前知道了每个人的意见,让一些人参与投票,在保证第一个人不离场的情况下最终剩余人数最多是多少Solution把和第一个意见不同的给去掉就行了voidsolve(){......
  • Chemistry Experiment Codeforces Round 247 (Div. 2) 线段树动态开点,二分
    第一次写的时候还不会线段树的动态开点,写了一个是线段树但是是\(O(N^2)\)的写法,现在用动态开点武装了自己,会了正解\(O(qlogn^2)\)。首先建立一个权值线段树,但这里的权值很大,通过动态开点去建树来节省空间,对于两种操作:操作1,常见的动态开点的单点修改操作2,二分答案,然后在线段树......
  • Codeforces 280C Game on Tree
    设\(p_i\)为\(i\)涂色或不涂色,\(1\)为涂,\(0\)为不涂,答案即为\(E[\sum_{i=1}^np_i]\)然后转化一下柿子:\(\sum_{i=1}^nE[p_i]\),这就很好求了,单独求每个点\(E[p_i]\)的值就行了考虑对于\(u\)点,\(p_u=1\),即能被涂需要满足其祖先都比其晚涂色假设现在有一个序列里面......
  • Educational Codeforces Round 147 (Rated for Div. 2) 题解
    ALink。模拟,代码。BLink。模拟,代码。CLink。我们设\(c\)为最后相同的字符。性质:我们一定不会删除字符\(c\)。因此以\(c\)为最后字符的操作次数就是不包含字符\(c\)的极大段的最小操作次数的最大值。对于一个长度为\(l(l\ge1)\)的段,它的最小操作次数为\(\l......
  • Codeforces 894D Ralph And His Tour in Binary Country
    预处理出对于\(u\)节点其子树内节点(包括\(u\))与\(u\)的距离,从小到大排序得到\(ds_u\)同时对\(ds_u\)进行前缀和处理\(dh_{u,i}=\sum\limits_{j=1}^{i}ds_{u,j}\)这样设\(tot\)为\(ds_u\)二分得到的\(ds_{u,i}\leh\)的右端点,也即为\(u\)子树内对答案有......
  • Codeforces Round 868 (Div. 2)
    题目链接C核心思路一定要看清楚题目,题目是要我们最小哦。首先看可不可抽像为数学表达式,答案肯定是可以的。x=p1^d1*p2^d2*..*pn^dn;D=\sum{(d1+1)*(d2+1)*(d3+1)*...(*(dn+1))};这个D表示的x的约数的个数,这个公式还是很好理解的,需要牢记。ans=D-n-1;为什么需要减一呢,因为......
  • Codeforces 1229B Kamil and Making a Stream
    \(\gcd\)一个性质:对于正整数\(x\),重复\(x\leftarrow\gcd(x,i)\)(\(i\ge0\))直到\(x=1\),\(x\)出现的值个数上限为\(\log_2(x)+1\)证明:考虑到\(x\)是逐渐变小,则在\(x\)变小的情况下,对于\(x=\prod_{i=1}^kp_i^{c_i}\)(\(p_i\\operatorname{为质数}\))中的\(c_i\)......
  • Codeforces Round 869 (Div. 1)
    C根据初中数学知识,恒成立问题考虑未知数x每一项的系数,然后得到(d+1)个等式,根据前两个就可以推出\(s=\frac{b_{d-1}-a_{d-1}}{da_d}\)且\(a_d=b_d\)但是一直不会用题目给的n个点值求出最高的两项系数(或它们的比值),并且怀疑是否把(d+1)个等式全部用到会更好做,然后两个方向分别搞了......
  • Codeforces Gym 103439D - LIS Counting(猜结论+状压)
    一道需要一些猜结论技巧的中档题。首先突破口在于排列长度恰好等于不是额外输入的某个数\(k\)而是LDS与LIS的乘积,这显然启示我们去找一些性质。根据dilworth定理,最长反链等于最小链覆盖,故LIS的长度,就是最少需要的递减数列的个数使得每个元素被覆盖至少一次,而每个递减数......