• 题解 [HNOI/AHOI2018]毒瘤


    题目传送门

    题目大意

    给出一个 (n) 个点 (m) 条边的无向图,问有多少个点集满足点集中任意两点均不存在边相连。

    (nle 10^5,m-nle 10),答案对 (998244353) 取模。

    思路

    妙啊!!!

    首先我们从树的形态开始考虑,你发现答案其实就是独立集的个数,具体来说我们可以设 (f_{u,0/1}) 表示 (u) 点选或不选的方案数,可以得到:

    [f_{u,0}=prod_{vin son_u} (f_{v,0}+f_{v,1}) ]

    [f_{u,1}=prod_{vin son_u} f_{v,0} ]

    然后我们考虑有返祖边的情况,你发现这个边数特别少,于是我们考虑枚举每一条边的情况,你发现只有 (3) 种,即 ((0,0),(0,1),(1,0)),但是实际上可以压缩到 (2) 种情况,即考虑一个点是 (0) 还是 (1)。于是我们可以枚举一下情况,然后时间复杂度就是 (Theta(2^{11} n))。就可以获得 (75) 的好成绩了。为了方便,我们之后称需要枚举情况的点为“不定点”。

    然后你发现实际上每次 dp,我们重复计算的东西实际上很多,但是我们经过思考发现其实每次只会改变“不定点”的虚树上的点。于是,我们只需要考虑虚树上儿子对父亲产生的贡献即可。

    然后我们发现实际上虚树上的儿子对父亲实际上可以表示为 (k_{v,0/1,0}f_{v,0}+k_{v,0/1,1}f_{v,1}),于是我们可以预处理出 (k_{v,0/1,0/1}) 这个系数,至于不在虚树上的儿子产生的贡献可以预处理出来。这个式子里面的常数是实际上就是不会改变的点对“不定点”产生的贡献造成的。

    然后你就发现这个东西其实跟动态 dp 的思想是差不对的,所以这道题实际上也可以用动态 dp 搞过去。

    时间复杂度 (Theta(n+s2^s)),其中 (s=m-n+1)

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define mod 998244353
    #define MAXN 100005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    int n,m;
    int mul (int a,int b){return 1ll * a * b % mod;}
    int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
    int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
    
    struct Coe{
    	int a,b;
    	Coe operator * (const int &p)const{return Coe {mul (a,p),mul (b,p)};}
    	Coe operator + (const Coe &p)const{return Coe {add (a,p.a),add (b,p.b)};}
    }k[MAXN][2];
    
    struct node{
    	int v;
    	Coe x,y;
    };
    
    vector <int> G[MAXN];
    vector <node> E[MAXN];
    
    void Add_Edge (int u,int v){
    	G[u].push_back (v),
    	G[v].push_back (u);
    }
    void Add_Edge1 (int u,int v,Coe w1,Coe w2){
    	E[u].push_back (node {v,w1,w2});
    }
    
    int ind,cnt,eu[MAXN],ev[MAXN],siz[MAXN],dfn[MAXN],mark[MAXN];
    
    void dfs (int u,int fa){
    	dfn[u] = ++ ind;
    	for (Int v : G[u]){
    		if (!dfn[v]) dfs (v,u),siz[u] += siz[v];
    		else if (v != fa){
    			mark[u] = 1;
    			if (dfn[u] < dfn[v]) eu[++ cnt] = u,ev[cnt] = v;
    		}
    	}
    	mark[u] |= siz[u] >= 2,siz[u] = siz[u] || mark[u];
    }
    
    int f[MAXN][2],p[MAXN][2],sur[MAXN][2],vis[MAXN];
    
    int dfs1 (int u){
    	p[u][0] = p[u][1] = vis[u] = 1;int pos = 0;
    	for (Int v : G[u]){
    		if (vis[v]) continue;
    		int w = dfs1 (v);
    		if (!w){
    			p[u][1] = mul (p[u][1],p[v][0]);
    			p[u][0] = mul (p[u][0],add (p[v][0],p[v][1]));
    		}
    		else if (mark[u]) Add_Edge1 (u,w,k[v][0] + k[v][1],k[v][0]);
    		else k[u][0] = k[v][0] + k[v][1],k[u][1] = k[v][0],pos = w;
    	}
    	if (mark[u]) k[u][0] = Coe {1,0},k[u][1] = Coe {0,1},pos = u;
    	else k[u][0] = k[u][0] * p[u][0],k[u][1] = k[u][1] * p[u][1];
    	return pos;  
    }
    
    void dfs2 (int u){
    	f[u][0] = sur[u][1] ? 0 : p[u][0],f[u][1] = sur[u][0] ? 0 : p[u][1];
    	for (node to : E[u]){
    		dfs2 (to.v);int v = to.v,p = f[v][0],q = f[v][1]; 
    		f[u][0] = mul (f[u][0],add (mul (to.x.a,p),mul (to.x.b,q)));
    		f[u][1] = mul (f[u][1],add (mul (to.y.a,p),mul (to.y.b,q)));
    	}
    }
    
    signed main(){
    	read (n,m);
    	for (Int i = 1,u,v;i <= m;++ i) read (u,v),Add_Edge (u,v);
    	dfs (1,0),mark[1] = 1,dfs1 (1);int up = 1 << cnt,ans = 0; 
    	for (Int S = 0;S < up;++ S){ 
    		for (Int i = 1;i <= cnt;++ i)
    			if (S >> i - 1 & 1) sur[eu[i]][1] = sur[ev[i]][0] = 1;
    			else sur[eu[i]][0] = 1;
    		dfs2 (1),ans = add (ans,add (f[1][0],f[1][1]));
    		for (Int i = 1;i <= cnt;++ i)
    			if (S >> i - 1 & 1) sur[eu[i]][1] = sur[ev[i]][0] = 0;
    			else sur[eu[i]][0] = 0;
    	}
    	write (ans),putchar ('
    ');
    	return 0;
    }
    
  • 相关阅读:
    asp搜索两个以上的词的原理
    ASP连接MYSQL数据库
    一个用ASP生成html的新方法
    文本框随文本的长度而增长
    ASP实现https和http之间转化
    ASP根据IP来判断跳转页面
    Access数据库在线压缩的实现方法
    aspjpeg 半透明描边的实现函数
    Session对象的集合
    react tsx
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13741948.html
Copyright © 2020-2023  润新知