poj 3417 Network
Description:
先给出一棵无根树,然后下面再给出m条边,把这m条边连上,然后每次你能毁掉两条边,规定一条是树边,一条是新边,问有多少种方案能使树断裂。
思路:
当你在树上连边的时候,树就会形成一个环,然后把在环内切边,那么第二刀任意。如果一条边在两个环内,第二刀唯一,如果在两个以上环内,无解。
也就是每次连边之后,求每条边的覆盖次数。应用树上差分(乱搞)算法可得解
#include<iostream> #include<vector> #include<cstring> #include<cstdio> using namespace std; const int N = 1e5 + 10, M = 2e5 + 10; typedef pair<int,int> P; int head[N], now; struct edges{ int to, next; }edge[N<<1]; void add(int u,int v){ edge[++now] = {v, head[u]}; head[u] = now;} int n, m, lca[N], fa[N], vis[N], w[N]; vector<P> q[N]; int get(int x){ if(x != fa[x]) return fa[x] = get(fa[x]); return x; } void tarjan(int x){ vis[x] = 1; for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(vis[v]) continue; tarjan(v); fa[v] = x; } for(int i = 0; i < q[x].size(); i++){ int v = q[x][i].first, id = q[x][i].second; if(vis[v] == 2) lca[id] = get(v); } vis[x] = 2; } int ans[N]; void dfs(int x){ ans[x] = w[x], vis[x] = 1; for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(vis[v]) continue; dfs(v); ans[x] += ans[v]; } } void init(){ memset(w,0,sizeof(w)); memset(edge,0,sizeof(edge)); now = 0; memset(head,0,sizeof(head)); memset(ans,0,sizeof(ans)); memset(vis,0,sizeof(vis)); memset(lca,0,sizeof(lca)); } int main(){ // freopen("wrong.in","r",stdin); while(scanf("%d%d", &n, &m) != EOF){ init(); int x, y; for(int i = 1; i <= n; i++) fa[i] = i; for(int i = 1; i < n; i++){ scanf("%d%d",&x, &y); add(x, y); add(y, x); } for(int i = 1; i <= m; i++){ scanf("%d%d",&x, &y); q[x].push_back(P(y, i)); q[y].push_back(P(x, i)); w[x]++, w[y]++; if(x == y) lca[i] = x; } tarjan(1); for(int i = 1; i <= m; i++) w[lca[i]] -= 2; memset(vis,0,sizeof(vis)); dfs(1); int tot = 0; for(int i = 2; i <= n; i++){ if(ans[i] == 0) tot += m; if(ans[i] == 1) tot++; } printf("%d\n",tot); for(int i = 1; i <= n; i++) q[i].clear(); } return 0; }