做题Codeforces Round #453 (Div. 1) D. Weighting a Tree——拆环
Posted cly_none
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了做题Codeforces Round #453 (Div. 1) D. Weighting a Tree——拆环相关的知识,希望对你有一定的参考价值。
前言:结论题似乎是我的硬伤……
题意是给你一个无向图,已知连接到每一个点的边的权值和(为整数,且属于区间[-n,n]),需要求出每条边权值的一个合法解(都要是在区间[-2*n^2,2*n^2]内的整数)。
第一个想法当然是O(n^2*m)的高斯消元。在此基础上,我想过通过选取某些节点,在边权总和中减去与之相邻的边,来逐个解出边的权值。这个本质上是优化解方程的办法难以适应全部情况,且难以通过编程实现。于是只能舍弃这个想法。
后来通过漫无边际的瞎想,观察标题,容易发现对于一棵树求解这个问题是极为容易的。于是下一个思路就是把这个无向图转化为一棵树。如下图所示,偶环的情况是很容易就能解决的。(无脑删边)
那么奇环怎么办呢?事实上,本人就卡在了这里。如果按照偶环的方法来解释奇环删边的合法性,我们发现最终有一个点的点权增加了2a。陷入僵局。这时,不妨让我们再考虑一下对树求解的过程。也就是一次dfs,对于除根结点之外的每一个结点都满足其权值和,再根据根结点是否满足其约束条件来判断是否有解。注意到上面奇环的操作,实质就意味着如果我们以一个奇环上的点为根结点,那么就可以在最后判断的时候任意加上一个偶数了。容易证明,最后与根结点相邻的边权和与其应有的边权和之差一定是一个偶数。也就是说,有奇环的图是一定有解的。因此,我们如上的处理奇环的方式,并不会影响解的存在性。
于是,我们就得到了处理环的方式:都不鸟它,并从奇环上随意拉一个点当根结点。
讲到这里,我们似乎还忽略了一个条件。
write a weight between - 2·n2 and 2·n2 (inclusive) on each edge
当然,这个范围是相当大的,一般而言解是一定在这个区间内的(也仅限一般而言)。基于cf是一个有hack机制的网站,毫无疑问会有数据把你的解卡出这个区间(对本人而言是test 34)。因此,random_shuffle是必不可缺的。
时间复杂度O(n+m)。
1 #include <bits/stdc++.h> 2 #define int long long 3 #define tag(i) (ed[((i)|1)>>1].id) 4 using namespace std; 5 const int N = 100010; 6 struct edge { 7 int la,b; 8 edge(int la=0,int b=0):la(la),b(b) {}; 9 } con[N<<1]; 10 int tot=1,fir[N]; 11 void add(int from,int to) { 12 con[++tot] = edge(fir[from],to); 13 fir[from] = tot; 14 con[++tot] = edge(fir[to],from); 15 fir[to] = tot; 16 } 17 int c[N],ans[N],n,m,cnt,dep[N],rt,ano,fat[N],up[N],mar[N]; 18 typedef pair<int,int> pii; 19 struct data { 20 int a,b,id; 21 data(int a=0,int b=0,int id=0):a(a),b(b),id(id){} 22 } ed[N]; 23 pii ext[N]; 24 bool vis[N]; 25 void dfs_init(int pos,int fa) { 26 fat[pos] = fa; 27 vis[pos] = 1; 28 dep[pos] = dep[fa] + 1; 29 for (int i = fir[pos] ; i ; i = con[i].la) { 30 if (con[i].b == fa) continue; 31 if (vis[con[i].b]) { 32 if (pos > con[i].b) ext[++cnt] = pii(pos,i); 33 } else dfs_init(con[i].b,pos),up[con[i].b] = tag(i); 34 } 35 } 36 int dfs(int pos,int fa) { 37 vis[pos] = 1; 38 int now = c[pos]; 39 for (int i = fir[pos] ; i ; i = con[i].la) { 40 if (vis[con[i].b]) continue; 41 now -= (ans[tag(i)] = dfs(con[i].b,pos)); 42 } 43 return now; 44 } 45 bool ocy(pii x) { 46 int a = x.first, b = con[x.second].b; 47 return (dep[a] + dep[b] + 1)&1; 48 } 49 void print() { 50 puts("YES"); 51 for (int i = 1 ; i <= m ; ++ i) { 52 cout << ans[i] << endl; 53 } 54 } 55 void modify(int x,int y) { 56 int k1 = 1, k2 = -1; 57 while (dep[x] > dep[y]) { 58 mar[up[x]] += k1; 59 k1 = -k1; 60 x = fat[x]; 61 } 62 while (dep[y] > dep[x]) { 63 mar[up[y]] += k2; 64 k2 = -k2; 65 y = fat[y]; 66 } 67 while (x != y) { 68 mar[up[x]] += k1; 69 k1 = -k1; 70 x = fat[x]; 71 mar[up[y]] += k2; 72 k2 = -k2; 73 y = fat[y]; 74 } 75 } 76 signed main() { 77 int a,b; 78 cin >> n >> m; 79 for (int i = 1 ; i <= n ; ++ i) cin>>c[i]; 80 for (int i = 1 ; i <= m ; ++ i) { 81 cin >> a >> b; 82 ed[i] = data(a,b,i); 83 } 84 random_shuffle(ed+1,ed+m+1); 85 for (int i = 1 ; i <= m ; ++ i) add(ed[i].a,ed[i].b); 86 dfs_init(1,0); 87 for (int i = 1 ; i <= cnt ; ++ i) { 88 if (ocy(ext[i])) { 89 rt = ext[i].first, ano = con[ext[i].second].b; 90 modify(rt,ano); 91 mar[tag(ext[i].second)] ++; 92 break; 93 } 94 } 95 memset (vis,0,sizeof vis); 96 if (rt) { 97 int uns = dfs(rt,0)>>1; 98 for (int i = 1 ; i <= m ; ++ i) ans[i] += mar[i] * uns; 99 print(); 100 } else { 101 if (dfs(1,0) != 0) puts("NO"); 102 else print(); 103 } 104 return 0; 105 }
小结:关于我卡在奇环无从下手,应该是缺乏与实际算法的运行相结合。
以上是关于做题Codeforces Round #453 (Div. 1) D. Weighting a Tree——拆环的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #453 (Div. 1)
Codeforces Round #453 (Div. 2) a-c
Codeforces Global Round 1 做题记录
Educational Codeforces Round 62 做题记录