[ARC105F]Lights Out on Connected Graph

Lights Out on Connected Graph

题解

对于边最后可以通过一系列操作变成红色的条件,我们可以理解成对节点进行染色。
由于所有边开始都是蓝色,最后都要变成红色,所以一条边的两个端点中有且只能有一个被染色。
由于合法的图是联通的,相当于存在能够覆盖在覆盖整个图的独立集。

首先看到 n ⩽ 17 n\leqslant 17 n17,很明显的状压了,考虑怎么状压。
我们定义 d p S dp_{S} dpS表示点集 S S S所对应的子图合法的方案数。
很明显,对于一个连边状态固定的联通子图,我们对于点的染色状态只有两种,恰好对应两个可以覆盖全图的独立集,两者之间是互补的。
而对于一个合法子图,它一定是联通的,所以它的染色方案是固定的。
但对于一个染色方案固定的子图,它的连边方案却有很多种,我们设对于点集 S S S,它的点集 T T T是一种颜色,那么它的连边方案有 2 ∣ E ( S ) − E ( T ) − E ( S − T ) ∣ 2^{|E(S)-E(T)-E(S-T)|} 2E(S)E(T)E(ST)种,可以通过枚举子集的方法得到。
我们用 g S g_{S} gS,表示点集为 S S S的子图的满足颜色条件的方案数。
容易得到转移方程式,
g S = ∑ T ∈ S 2 E ( S ) − E ( T ) − E ( S − T ) g_{S}=\sum_{T\in S}2^{E(S)-E(T)-E(S-T)} gS=TS2E(S)E(T)E(ST)

在这些连边方案中,有部分方案是不联通的,它们对应的染色方案自然也不是 2 2 2种,所以我们要先将这些方案去掉,再除以二,得到 d p S dp_{S} dpS
由于这些方案是不连通的,所以我们自然也可以用枚举子集的方法得到答案。
h S h_{S} hS表示点集为 S S S的子图的不满足连通性的子图数,容易得到状态转移方程式,
h S = ∑ T ⊂ S 2 d p T h S − T h_{S}=\sum_{T\subset S}2dp_{T}h_{S-T} hS=TS2dpThST
但如果我们直接枚举所有子集,自然会因为不同连通块被枚举到的顺序问题产生重复,所以我们需要在枚举时保证连通块加入的顺序时固定的,如添加 l o w b i t ( T ) = l o w b i t ( S ) lowbit(T)=lowbit(S) lowbit(T)=lowbit(S)这样的条件。

很明显, d p S dp_{S} dpS可以通过两者相减得到, d p S = g S − h S 2 dp_{S}=\frac{g_{S}-h_{S}}{2} dpS=2gShS
我们的答案即为 d p V dp_{V} dpV
时间复杂度 O ( 3 n ) O\left(3^n\right) O(3n)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define MAXN (1<<17)+5
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int lim=15;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,dp[MAXN],g[MAXN],h[MAXN],sum[MAXN],pow2[505],hig[MAXN];
signed main(){
	read(n);read(m);pow2[0]=h[0]=1;
	for(int i=1;i<=m;i++){
		int u,v;read(u);read(v);pow2[i]=add(pow2[i-1],pow2[i-1],mo);
		for(int j=1;j<(1<<n);j++)if((j&(1<<u-1))&&(j&(1<<v-1)))sum[j]++;
	}
	for(int i=1;i<=n;i++)
		for(int j=0;j<(1<<i-1);j++)hig[(1<<i-1)|j]=(1<<i-1);
	for(int i=1;i<(1<<n);i++){
		for(int j=i;j&hig[i];j=i&(j-1))g[i]=add(g[i],2ll*pow2[sum[i]-sum[j]-sum[i-j]]%mo,mo);
		for(int j=i&(i-1);j&hig[i];j=i&(j-1))h[i]=add(h[i],1ll*dp[j]*h[i-j]%mo,mo);
		dp[i]=add(g[i],mo-h[i],mo);h[i]=add(h[i],dp[i],mo);
	}
	printf("%d\n",1ll*dp[(1<<n)-1]*inv2%mo);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值