JZOJ 5798. 2018.08.11【2018提高组】模拟A组 树

我们有一颗从1到n编号的n个结点的树,此外,您将从树中获得M个节点对,形式为(a1,b1),(a2,b2),…(am,bm).

我们需要给每一条边定向,使得每一对节点对存在一条从ai到bi或从bi到ai的路径。

现在要求方案数,对10^9+7取mod即可。

对于前20%的数据,保证是一条链。

另有40%的数据,n,m<=5000

对于100%的数据,n,m<=300000

以前似乎见过这道题……然后就咕咕咕了……

一个十分显然的事实,固定根节点(1号节点)后,路径的方向只有向上和向下

不妨想到将链进行合并

对于一对关系(u,v),设它们的lca是d,那么就是将u~d合并,v~d合并(d的第一个儿子是最后合并)

处理完之后再合并(u,v),带权1

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 3e5 + 10;
 4 
 5 struct REL { int a, b; } rel[N];
 6 
 7 int n, m;
 8 
 9 int head[N], rest[N * 2], to[N * 2], tot;
10 
11 void add(int u, int v) { to[++ tot] = v, rest[tot] = head[u], head[u] = tot; }
12 
13 int fa[N][21], dep[N];
14 
15 void dfs(int u, int f) {
16     fa[u][0] = f, dep[u] = dep[f] + 1;
17     for(int i = head[u] ; i ;  i = rest[i]) {
18         int v = to[i];
19         if(v == f) continue;
20         dfs(v, u);
21     }
22 }
23 
24 int getlca(int u, int v) {
25     if(dep[u] < dep[v]) swap(u, v);
26     for(int i = 20 ; ~ i ; -- i) if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];
27     if(u == v) return u;
28     for(int i = 20 ; ~ i ; -- i) if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
29     return fa[u][0];
30 }
31 
32 int dis[N], acc[N];
33 
34 int get(int x) {
35     if(x == acc[x]) return x;
36     int y = acc[x];
37     acc[x] = get(acc[x]);
38     dis[x] ^= dis[y];
39     return acc[x];
40 }
41 
42 void merge(int a, int d) {
43     a = get(a);
44     while(dep[a] >= dep[d] + 2) {
45         acc[a] = get(fa[a][0]);
46         a = get(a);
47     }
48 }
49 
50 const int p = 1e9 + 7;
51 typedef long long ll;
52 ll pw(ll a, ll b) {
53     ll r = 1;
54     for( ; b ; b >>= 1, a = a * a % p) if(b & 1) r = r * a % p;
55     return r;
56 }
57 
58 int main() {
59     freopen("usmjeri.in", "r", stdin);
60     freopen("usmjeri.out", "w", stdout);
61     cin >> n >> m;
62     for(int i = 1, u, v ; i < n ; ++ i) {
63         cin >> u >> v;
64         add(u, v), add(v, u);
65     }
66     for(int i = 1 ; i <= n ; ++ i) acc[i] = i;
67     dfs(1, 0);
68     for(int j = 1 ; j <= 20 ; ++ j)
69         for(int i = 1 ; i <= n ; ++ i)
70             fa[i][j] = fa[fa[i][j - 1]][j - 1];
71     for(int i = 1 ; i <= m ; ++ i) {
72         cin >> rel[i].a >> rel[i].b;
73         int a = rel[i].a, b = rel[i].b, d = getlca(a, b);
74         merge(a, d);
75         merge(b, d);
76     }
77     for(int i = 1 ; i <= m ; ++ i) {
78         int a = rel[i].a, b = rel[i].b, fa = get(a), fb = get(b), d = getlca(a, b);
79         if(d == a || d == b) continue;
80         if(fa == fb) {
81             if((dis[a] ^ dis[b]) == 0) {
82                 puts("0");
83                 return 0;
84             }
85         } else {
86             dis[fa] = dis[a] ^ dis[b] ^ 1;
87             acc[fa] = get(b);
88             
89         }
90     }
91     int ans = 0;
92     for(int i = 2 ; i <= n ; ++ i) {
93         ans += get(i) == i;
94     }
95     cout << pw(2, ans) << endl;
96 }
JZOJ 5798. 2018.08.11【2018提高组】模拟A组 树

转载于:https://www.cnblogs.com/KingSann/articles/9461935.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值