【做题】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 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 }

 

小结:关于我卡在奇环无从下手,应该是缺乏与实际算法的运行相结合。

posted @ 2017-12-24 22:36  莫名其妙的aaa  阅读(276)  评论(0编辑  收藏  举报