【做题】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)。
#include <bits/stdc++.h>
#define int long long
#define tag(i) (ed[((i)|1)>>1].id)
using namespace std;
const int N = ;
struct edge {
int la,b;
edge(int la=,int b=):la(la),b(b) {};
} con[N<<];
int tot=,fir[N];
void add(int from,int to) {
con[++tot] = edge(fir[from],to);
fir[from] = tot;
con[++tot] = edge(fir[to],from);
fir[to] = tot;
}
int c[N],ans[N],n,m,cnt,dep[N],rt,ano,fat[N],up[N],mar[N];
typedef pair<int,int> pii;
struct data {
int a,b,id;
data(int a=,int b=,int id=):a(a),b(b),id(id){}
} ed[N];
pii ext[N];
bool vis[N];
void dfs_init(int pos,int fa) {
fat[pos] = fa;
vis[pos] = ;
dep[pos] = dep[fa] + ;
for (int i = fir[pos] ; i ; i = con[i].la) {
if (con[i].b == fa) continue;
if (vis[con[i].b]) {
if (pos > con[i].b) ext[++cnt] = pii(pos,i);
} else dfs_init(con[i].b,pos),up[con[i].b] = tag(i);
}
}
int dfs(int pos,int fa) {
vis[pos] = ;
int now = c[pos];
for (int i = fir[pos] ; i ; i = con[i].la) {
if (vis[con[i].b]) continue;
now -= (ans[tag(i)] = dfs(con[i].b,pos));
}
return now;
}
bool ocy(pii x) {
int a = x.first, b = con[x.second].b;
return (dep[a] + dep[b] + )&;
}
void print() {
puts("YES");
for (int i = ; i <= m ; ++ i) {
cout << ans[i] << endl;
}
}
void modify(int x,int y) {
int k1 = , k2 = -;
while (dep[x] > dep[y]) {
mar[up[x]] += k1;
k1 = -k1;
x = fat[x];
}
while (dep[y] > dep[x]) {
mar[up[y]] += k2;
k2 = -k2;
y = fat[y];
}
while (x != y) {
mar[up[x]] += k1;
k1 = -k1;
x = fat[x];
mar[up[y]] += k2;
k2 = -k2;
y = fat[y];
}
}
signed main() {
int a,b;
cin >> n >> m;
for (int i = ; i <= n ; ++ i) cin>>c[i];
for (int i = ; i <= m ; ++ i) {
cin >> a >> b;
ed[i] = data(a,b,i);
}
random_shuffle(ed+,ed+m+);
for (int i = ; i <= m ; ++ i) add(ed[i].a,ed[i].b);
dfs_init(,);
for (int i = ; i <= cnt ; ++ i) {
if (ocy(ext[i])) {
rt = ext[i].first, ano = con[ext[i].second].b;
modify(rt,ano);
mar[tag(ext[i].second)] ++;
break;
}
}
memset (vis,,sizeof vis);
if (rt) {
int uns = dfs(rt,)>>;
for (int i = ; i <= m ; ++ i) ans[i] += mar[i] * uns;
print();
} else {
if (dfs(,) != ) puts("NO");
else print();
}
return ;
}
小结:关于我卡在奇环无从下手,应该是缺乏与实际算法的运行相结合。
【做题】Codeforces Round #453 (Div. 1) D. Weighting a Tree——拆环的更多相关文章
- Codeforces Round #453 (Div. 1) D. Weighting a Tree(构造)
题意 一个 \(n\) 个点 \(m\) 条边的无向连通图中每个点都有一个权值,现在要求给每条边定一个权值,满足每个点的权值等于所有相连的边权之和,权值可负. 题解 如果图是一棵树,那么方案就是唯一的 ...
- Codeforces Round #453 (Div. 1)
Codeforces Round #453 (Div. 1) A. Hashing Trees 题目描述:给出一棵树的高度和每一层的节点数,问是否有两棵树都满足这个条件,若有,则输出这两棵树,否则输出 ...
- 水题 Codeforces Round #308 (Div. 2) A. Vanya and Table
题目传送门 /* 水题:读懂题目就能做 */ #include <cstdio> #include <iostream> #include <algorithm> ...
- 水题 Codeforces Round #302 (Div. 2) A Set of Strings
题目传送门 /* 题意:一个字符串分割成k段,每段开头字母不相同 水题:记录每个字母出现的次数,每一次分割把首字母的次数降为0,最后一段直接全部输出 */ #include <cstdio> ...
- 水题 Codeforces Round #299 (Div. 2) A. Tavas and Nafas
题目传送门 /* 很简单的水题,晚上累了,刷刷水题开心一下:) */ #include <bits/stdc++.h> using namespace std; ][] = {" ...
- 水题 Codeforces Round #304 (Div. 2) A. Soldier and Bananas
题目传送门 /* 水题:ans = (1+2+3+...+n) * k - n,开long long */ #include <cstdio> #include <algorithm ...
- 水题 Codeforces Round #303 (Div. 2) A. Toy Cars
题目传送门 /* 题意:5种情况对应对应第i或j辆车翻了没 水题:其实就看对角线的上半边就可以了,vis判断,可惜WA了一次 3: if both cars turned over during th ...
- 水题 Codeforces Round #286 (Div. 2) A Mr. Kitayuta's Gift
题目传送门 /* 水题:vector容器实现插入操作,暴力进行判断是否为回文串 */ #include <cstdio> #include <iostream> #includ ...
- 构造水题 Codeforces Round #206 (Div. 2) A. Vasya and Digital Root
题目传送门 /* 构造水题:对于0的多个位数的NO,对于位数太大的在后面补0,在9×k的范围内的平均的原则 */ #include <cstdio> #include <algori ...
随机推荐
- 【转】Spotlight实时监控Windows Server 2008
Windows Server 2008作为服务器平台已逐渐被推广和应用,丰富的功能和良好的稳定性为其赢得了不错的口碑.但是和Windows Server 2003相比,其系统的自我监控功能并没有多大的 ...
- 20155228 2016-2017-2 《Java程序设计》第10周学习总结
20155228 2016-2017-2 <Java程序设计>第10周学习总结 教材学习内容总结 网络 网络是能够波此通信的计算机的集合根据范}到的宽度,网络可以分为局域网和广域网.LAN ...
- laravel 服务容器实例——深入理解IoC模式
刚刚接触laravel,对于laravel的服务容器不是很理解.看了<Laravel框架关键技术解析>和网上的一些资料后对于服务容器有了一些自己的理解,在这里分享给大家 1.依赖 IoC模 ...
- locust 的使用
Contents Locust这一款开源性能测试工具.然而,当前在网络上针对Locust的教程极少,不管是中文还是英文,基本都是介绍安装方法和简单的测试案例演示,但对于较复杂测试场景的案例演示却基本没 ...
- linux基础命令---显示进程ps
ps ps指令可以显示系统中当前进程的信息,它的输出结果是高度可定制的.如果您希望重复更新所选内容和显示的信息,请使用top(1)代替. 请注意,“ps-aux”与“ps aux”不同.POSIX和U ...
- QT中添加 动态库(.so) 和 静态库 (.a) 的方法
在QT 的Makefile文件中: 1 添加动态库,如lipcap.so 则,在LIBS一行中添加“-L/usr/local/lib -lpcap”,依据自己的情况修改libpcap.so的路径 2 ...
- [转载] Oracle之内存结构(SGA、PGA)
2011-05-10 14:57:53 分类: Linux 一.内存结构 SGA(System Global Area):由所有服务进程和后台进程共享: PGA(Program Global Area ...
- Java 高级开发必修知识---内部类
摘自:http://www.cnblogs.com/lsy131479/p/8798912.html Java 内部类分为: 1)成员内部类 2)静态嵌套类 3)方法内部类 4)匿名内部类 内部类的共 ...
- 已知宽高和未知宽高的div块的水平垂直居中
//已知宽高的情况 .div1_container{ border:1px solid #00ee00; height:300px; position:relative; } ...
- 单例设计模式 --c#
单例设计模式:在单例设计模式中我们要保持对象始终是唯一的 参考代码: class SingleObject { private SingleObject() { } private static Si ...