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

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

时间: 2024-10-13 11:42:47

【做题】Codeforces Round #453 (Div. 1) D. Weighting a Tree——拆环的相关文章

水题 Codeforces Round #286 (Div. 2) A Mr. Kitayuta&#39;s Gift

题目传送门 1 /* 2 水题:vector容器实现插入操作,暴力进行判断是否为回文串 3 */ 4 #include <cstdio> 5 #include <iostream> 6 #include <algorithm> 7 #include <cstring> 8 #include <string> 9 #include <vector> 10 using namespace std; 11 12 const int MAXN

水题 Codeforces Round #308 (Div. 2) A. Vanya and Table

题目传送门 1 /* 2 水题:读懂题目就能做 3 */ 4 #include <cstdio> 5 #include <iostream> 6 #include <algorithm> 7 #include <cstring> 8 #include <cmath> 9 #include <vector> 10 #include <string> 11 #include <queue> 12 #include

水题 Codeforces Round #303 (Div. 2) A. Toy Cars

题目传送门 1 /* 2 题意:5种情况对应对应第i或j辆车翻了没 3 水题:其实就看对角线的上半边就可以了,vis判断,可惜WA了一次 4 3: if both cars turned over during the collision. 5 是指i,j两辆车,而不是全部 6 */ 7 #include <cstdio> 8 #include <algorithm> 9 #include <cstring> 10 #include <cmath> 11 #

水题 Codeforces Round #299 (Div. 2) A. Tavas and Nafas

题目传送门 1 /* 2 很简单的水题,晚上累了,刷刷水题开心一下:) 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 7 char s1[11][10] = {"zero", "one", "two", "three", "four", "five", "six", "seven

水题 Codeforces Round #302 (Div. 2) A Set of Strings

题目传送门 1 /* 2 题意:一个字符串分割成k段,每段开头字母不相同 3 水题:记录每个字母出现的次数,每一次分割把首字母的次数降为0,最后一段直接全部输出 4 */ 5 #include <cstdio> 6 #include <iostream> 7 #include <cstring> 8 #include <string> 9 #include <algorithm> 10 using namespace std; 11 12 con

贪心/思维题 Codeforces Round #310 (Div. 2) C. Case of Matryoshkas

题目传送门 1 /* 2 题意:套娃娃,可以套一个单独的娃娃,或者把最后面的娃娃取出,最后使得0-1-2-...-(n-1),问最少要几步 3 贪心/思维题:娃娃的状态:取出+套上(2),套上(1), 已套上(0),先从1开始找到已经套好的娃娃层数, 4 其他是2次操作,还要减去k-1个娃娃是只要套上就可以 5 详细解释:http://blog.csdn.net/firstlucker/article/details/46671251 6 */ 7 #include <cstdio> 8 #i

水题 Codeforces Round #304 (Div. 2) A. Soldier and Bananas

题目传送门 1 /* 2 水题:ans = (1+2+3+...+n) * k - n,开long long 3 */ 4 #include <cstdio> 5 #include <algorithm> 6 #include <cstring> 7 #include <cmath> 8 using namespace std; 9 10 typedef long long ll; 11 12 int main(void) //Codeforces Roun

水题 Codeforces Round #303 (Div. 2) D. Queue

题目传送门 1 /* 2 比C还水... 3 */ 4 #include <cstdio> 5 #include <algorithm> 6 #include <cstring> 7 #include <cmath> 8 #include <iostream> 9 using namespace std; 10 11 typedef long long ll; 12 13 const int MAXN = 1e5 + 10; 14 const i

codeforces水题100道 第十八题 Codeforces Round #289 (Div. 2, ACM ICPC Rules) A. Maximum in Table (brute force)

题目链接:http://www.codeforces.com/problemset/problem/509/A题意:f[i][1]=f[1][i]=1,f[i][j]=f[i-1][j]+f[i][j-1],求f[n][n].C++代码: #include <iostream> using namespace std; int n, f[11][11]; int main() { cin >> n; for (int i=1;i<=n;i++) f[i][1] = f[1][