zoukankan      html  css  js  c++  java
  • 题解 [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;
    }
    
  • 相关阅读:
    (二分查找 拓展) leetcode 69. Sqrt(x)
    (二分查找 拓展) leetcode 162. Find Peak Element && lintcode 75. Find Peak Element
    (链表) lintcode 219. Insert Node in Sorted Linked List
    (二分查找 拓展) leetcode 34. Find First and Last Position of Element in Sorted Array && lintcode 61. Search for a Range
    (最短路 Floyd) P2910 [USACO08OPEN]寻宝之路Clear And Present Danger 洛谷
    (字符串 数组 递归 双指针) leetcode 344. Reverse String
    (二叉树 DFS 递归) leetcode 112. Path Sum
    (二叉树 DFS 递归) leetcode 101. Symmetric Tree
    (二叉树 递归) leetcode 144. Binary Tree Preorder Traversal
    (二叉树 递归 DFS) leetcode 100. Same Tree
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13741948.html
Copyright © 2011-2022 走看看