卧薪尝胆,厚积薄发。
提高A组模拟 树
Date: Sun Aug 12 16:10:06 CST 2018 In Category: NoCategory

Description:

有一颗 $n$ 个结点的树, $M$ 个节点对,给每一条边定向,使得每一对节点对存在一条从 $a_i$ 到 $b_i$ 或从 $b_i$ 到 $a_i$ 的路径,求方案数。

Solution:

首先发现一条路径中只要有一条边方向确定其他边的方向就都确定了,于是可以把这些边用并查集合并起来,并在之后合并时用并查集跳过一些已经连在一起的边,对于 $LCA$ 的两边,可以用带权并查集连一条权值为 $1$ 的边表示两条路径不能同时指向根或同时从根指出来,但不能一边操作一边判断,因为在中途很多边还没有被连起来在不停向上跳连边的时候可能会出问题,因为在同一条链上的边的方向不一定是相同的,最好的处理办法是先把所有端点到 $LCA$ 的部分连起来,在最后的时候一边判断一边把两条链合并,这样就不会有问题了。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 300010
struct edge
{
int to,nxt;
}e[MAXN << 1];
int edgenum = 0;
int lin[MAXN] = {0};
void add(int a,int b)
{
++edgenum;e[edgenum].to = b;e[edgenum].nxt = lin[a];lin[a] = edgenum;
++edgenum;e[edgenum].to = a;e[edgenum].nxt = lin[b];lin[b] = edgenum;
return;
}
int in[MAXN][3];
int f[MAXN],g[MAXN];
int get(int x){return (f[x] == 0 ? 0 : (g[x] ^ get(f[x])));}
int find(int x)
{
if(f[x] == 0)return x;
int tmp = f[x];
f[x] = find(f[x]);
g[x] = g[tmp] ^ g[x];
return f[x];
}
int top[MAXN],dep[MAXN],siz[MAXN],fa[MAXN],son[MAXN];
void dfs1(int k,int depth)
{
dep[k] = depth;siz[k] = 1;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to != fa[k])
{
fa[e[i].to] = k;
dfs1(e[i].to,depth + 1);
siz[k] += siz[e[i].to];
if(son[k] == 0 || siz[e[i].to] > siz[son[k]])
{
son[k] = e[i].to;
}
}
}
return;
}
void dfs2(int k,int tp)
{
top[k] = tp;
if(son[k] == 0)return;
dfs2(son[k],tp);
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to != fa[k] && e[i].to != son[k])
{
dfs2(e[i].to,e[i].to);
}
}
return;
}
int LCA(int a,int b)
{
while(top[a] != top[b])
{
if(dep[top[a]] < dep[top[b]])swap(a,b);
a = fa[top[a]];
}
return dep[a] < dep[b] ? a : b;
}
#define MOD 1000000007
typedef long long ll;
ll power(ll a,int b)
{
ll res = 1;
while(b > 0)
{
if(b & 1)res = res * a % MOD;
a = a * a % MOD;
b = b >> 1;
}
return res;
}
int main()
{
freopen("usmjeri.in","r",stdin);
freopen("usmjeri.out","w",stdout);
scanf("%d%d",&n,&m);
int a,b;
int cnt = n - 1;
for(int i = 1;i < n;++i)
{
scanf("%d%d",&a,&b);
add(a,b);
}
dfs1(1,1);dfs2(1,1);
bool tag = true;
for(int i = 1;i <= m;++i)
{
scanf("%d%d",&in[i][1],&in[i][2]);
in[i][0] = LCA(in[i][1],in[i][2]);//cout << lca << endl;
int k = in[i][1];
while(true)
{
k = find(k);
if(dep[k] <= dep[in[i][0]] + 1)break;
f[k] = fa[k];//cout << k << " " << fa[k] << " " << find(fa[k]) << endl;
--cnt;
}//cout << f[740] << endl;
k = in[i][2];
while(true)
{
k = find(k);
if(dep[k] <= dep[in[i][0]] + 1)break;
f[k] = fa[k];//cout << k << " " << fa[k] << " " << find(fa[k]) << endl;
--cnt;
}//cout << f[740] << endl;
}
for(int i = 1;i <= m;++i)
{
if(in[i][0] == in[i][1] || in[i][0] == in[i][2])continue;
int p = find(in[i][1]),q = find(in[i][2]);
if(p == q)
{
if(get(in[i][1]) == get(in[i][2]))
{
tag = false;
break;
}
}
else
{
g[p] = 1 ^ get(in[i][1]) ^ get(in[i][2]);
f[p] = q;
--cnt;
}
}
if(!tag)
{
puts("0");
}
else
{
cout << power(2,cnt) << endl;
}
fclose(stdin);
fclose(stdout);
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡