BZOJ

CodeVS

Uoj

题目大意:

给一个n个点的边带权树,给定m条链,你可以选择树中的任意一条边,将它置为0,使得最长的链长最短。

题目分析:

最小化最大值,二分。

二分最短长度mid,将图中链长大于mid的链提取出来,求他们的交路径,选择他们都经过最大的一条边,看是否满足要求。

这是基本思路,下面来想想如何求解:一共尝试了两种办法:

  • 倍增lca / 树链剖分lca + 树上差分: 求lca部分略过(本题用链剖较快),对于一条u->v的路径,在u处+1,v处+1,lca处-2,从下往上求后缀和,即可得到i->fa[i]这条边被经过的次数,假设二分时选出了k条边,那么选择一条经过次数==k的边权最大的边,来判断是否满足。

  • 树链剖分 + 线段树:将u->v上每个节点+1,选出所有的边后,在线段树上将标记下放到底层(节点),如果该节点i(代表i->fa[i]这条边)值为k,更新边权最大值。最后用这个最大值来判断是否满足。注意:pathModify时,最后一步modify时不能改lca(看代码吧,一言难尽,仔细脑补),因为lca代表的边不是这条路径上的。

大优化: 开一个记忆化数组memory,记录选出最大的k条边时的最大权值和,可以加速很多。

ps: 被uoj的extra test hack掉了。

code

树链lca + 树上差分:

#include<bits/stdc++.h>
using namespace std; typedef long long ll; namespace IO{
inline ll read(){
ll i = 0, f = 1; char ch = getchar();
for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
if(ch == '-') f = -1, ch = getchar();
for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
return i * f;
}
inline void wr(ll x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
}using namespace IO; typedef long long ll;
const int N = 3e5 + 5, M = 3e5 + 5, OO = 0x3f3f3f3f;
int n, m, ecnt, adj[N], nxt[N << 1], go[N << 1];
int dep[N], sum[N], fa[N], sze[N], idx[N], pos[N], tot, son[N], top[N];
ll ans, len[N << 1], l = OO, r, dis[N], val[N], memory[M];
struct node{
int u, v;
ll tt;
inline bool operator < (const node &b) const{return tt < b.tt;}
}plan[M]; inline void addEdge(int u, int v, ll t){nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v, len[ecnt] = t;} inline int getLca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
if(u == v) return u;
return dep[u] < dep[v] ? u : v;
} inline void dfs1(int u, int f){
dep[u] = dep[f] + 1, fa[u] = f, sze[u] = 1;
for(int e = adj[u], v; e; e = nxt[e]){
if((v = go[e]) == f) continue;
dis[v] = dis[u] + len[e], val[v] = len[e], dfs1(v, u), sze[u] += sze[v];
if(sze[v] > sze[son[u]]) son[u] = v;
}
} inline void dfs2(int u, int f){
if(son[u]){
idx[pos[son[u]] = ++tot] = son[u];
top[son[u]] = top[u];
dfs2(son[u], u);
}
for(int v, e = adj[u]; e; e = nxt[e]){
if((v = go[e]) == f || v == son[u]) continue;
top[v] = v;
idx[pos[v] = ++tot] = v;
dfs2(v, u);
}
} inline void getSum(int u, int f){
for(int v, e = adj[u]; e; e = nxt[e]){
if((v = go[e]) == f) continue;
getSum(v, u);
sum[u] += sum[v];
}
} inline bool check(ll mid){
memset(sum, 0, sizeof sum); int cnt = 0;
for(int i = m; i >= 1; i--){
if(plan[i].tt <= mid) break;
cnt++, sum[plan[i].u]++, sum[plan[i].v]++, sum[getLca(plan[i].u, plan[i].v)] -= 2;
}
if(memory[cnt])
return plan[m].tt - memory[cnt] <= mid;
getSum(1, 0);
ll maxx = 0;
for(int i = 1; i <= n; i++) if(sum[i] == cnt) maxx = max(maxx, val[i]);
memory[cnt] = maxx;
return plan[m].tt - maxx <= mid;
} inline void solve(){
l = 0, r = plan[m].tt;
while(l <= r){
ll mid = l + r >> 1;
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
} int main(){
n = read(), m = read();
for(int i = 1; i < n; i++){
int x = read(), y = read(); ll t = 1ll*read();
addEdge(x, y, t), addEdge(y, x, t);
}
pos[1] = top[1] = idx[1] = tot = 1, dfs1(1, 0), dfs2(1, 0);
for(int i = 1; i <= m; i++) plan[i].u = read(), plan[i].v = read(), plan[i].tt = dis[plan[i].u] + dis[plan[i].v] - 2 * dis[getLca(plan[i].u, plan[i].v)];
sort(plan + 1, plan + m + 1);
solve();
wr(ans), putchar('\n');
return 0;
}

树链剖分 + 线段树:

#include<bits/stdc++.h>
using namespace std; typedef long long ll; namespace IO{
inline ll read(){
ll i = 0, f = 1; char ch = getchar();
for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
if(ch == '-') f = -1, ch = getchar();
for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
return i * f;
}
inline void wr(ll x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
}using namespace IO; typedef long long ll;
const int N = 3e5 + 5, M = 3e5 + 5, OO = 0x3f3f3f3f;
int n, m, ecnt, adj[N], nxt[N << 1], go[N << 1];
int dep[N], sum[N], fa[N], sze[N], idx[N], pos[N], tot, son[N], top[N];
ll ans, len[N << 1], l = OO, r, dis[N], val[N], memory[M];
struct node{
int u, v;
ll tt;
inline bool operator < (const node &b) const{return tt < b.tt;}
}plan[M]; namespace SegTree{
int tree[N << 2], tag[N << 2];
inline void upt(int k){tree[k] = tree[k << 1] + tree[k << 1 | 1];}
inline void add(int k, int l, int r, int v){tree[k] += (r - l + 1) * v, tag[k] += v;}
inline void modify(int k, int l, int r, int x, int y){
if(x <= l && r <= y){add(k, l, r, 1);return;}
int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
if(x <= mid) modify(lc, l, mid, x, y);
if(y > mid) modify(rc, mid + 1, r, x, y);
upt(k);
}
inline void pushDown(int k, int l, int r){if(tag[k]) add(k << 1, l, l + r >> 1, tag[k]), add(k << 1 | 1, (l + r >> 1) + 1, r, tag[k]), tag[k] = 0;}
inline void pushToTheEnd(int k, int l, int r, ll v, ll &maxx){
if(l == r){if(tree[k] == v) maxx = max(maxx, val[idx[l]]);return;}
int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
pushDown(k, l, r);
pushToTheEnd(lc, l, mid, v, maxx);
pushToTheEnd(rc, mid + 1, r, v, maxx);
}
}using namespace SegTree; inline void addEdge(int u, int v, ll t){nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v, len[ecnt] = t;} inline int getLca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
if(u == v) return u;
return dep[u] < dep[v] ? u : v;
} inline void dfs1(int u, int f){
dep[u] = dep[f] + 1, fa[u] = f, sze[u] = 1;
for(int e = adj[u], v; e; e = nxt[e]){
if((v = go[e]) == f) continue;
dis[v] = dis[u] + len[e], val[v] = len[e], dfs1(v, u), sze[u] += sze[v];
if(sze[v] > sze[son[u]]) son[u] = v;
}
} inline void dfs2(int u, int f){
if(son[u]){
idx[pos[son[u]] = ++tot] = son[u];
top[son[u]] = top[u];
dfs2(son[u], u);
}
for(int v, e = adj[u]; e; e = nxt[e]){
if((v = go[e]) == f || v == son[u]) continue;
top[v] = v;
idx[pos[v] = ++tot] = v;
dfs2(v, u);
}
} inline void pathModify(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
modify(1, 1, n, pos[top[u]], pos[u]);
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u, v);
modify(1, 1, n, pos[son[u]], pos[v]);
} inline bool check(ll mid){
int cnt = 0;
memset(tree, 0, sizeof tree);
memset(tag, 0, sizeof tag);
for(int i = m; i >= 1; i--){
if(plan[i].tt <= mid) break;
cnt++;
}
if(memory[cnt])
return plan[m].tt - memory[cnt] <= mid;
for(int i = m; i >= m - cnt + 1; i--) pathModify(plan[i].u, plan[i].v);
ll maxx = 0;
pushToTheEnd(1, 1, n, cnt, maxx);
memory[cnt] = maxx;
return plan[m].tt - maxx <= mid;
} inline void solve(){
l = 0, r = plan[m].tt;
while(l <= r){
ll mid = l + r >> 1;
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
} int main(){
n = read(), m = read();
for(int i = 1; i < n; i++){
int x = read(), y = read(); ll t = 1ll*read();
addEdge(x, y, t), addEdge(y, x, t);
}
pos[1] = top[1] = idx[1] = tot = 1, dfs1(1, 0), dfs2(1, 0);
for(int i = 1; i <= m; i++) plan[i].u = read(), plan[i].v = read(), plan[i].tt = dis[plan[i].u] + dis[plan[i].v] - 2 * dis[getLca(plan[i].u, plan[i].v)];
sort(plan + 1, plan + m + 1);
solve();
wr(ans), putchar('\n');
return 0;
}

NOIP2015 运输计划 - 二分 + 树链剖分 / (倍增 + 差分)的更多相关文章

  1. bzoj 4326: NOIP2015 运输计划【树链剖分+二分+树上差分】

    常数巨大,lg上开o2才能A 首先预处理出运输计划的长度len和lca,然后二分一个长度w,对于长度大于w的运输计划,在树上差分(d[u]+1,d[v]+1,d[lca]-2),然后dfs,找出所有覆 ...

  2. Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)

    Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...

  3. 【NOIP 2015 DAY2 T3】 运输计划 (树链剖分-LCA)

    题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家 ...

  4. [noip 2015]运输计划 [LCA][树链剖分]

    用了luogu上的题目描述 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的 ...

  5. BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

  6. [BZOJ1146][CTSC2008]网络管理Network(二分+树链剖分+线段树套平衡树)

    题意:树上单点修改,询问链上k大值. 思路: 1.DFS序+树状数组套主席树 首先按照套路,关于k大值的问题,肯定要上主席树,每个点维护一棵权值线段树记录它到根的信息. 关于询问,就是Que(u)+Q ...

  7. BZOJ_4326_[NOIP2015]_运输计划_(二分+LCA_树链剖分/Tarjan+差分)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=4326 给出一棵带有边权的树,以及一系列任务,任务是从树上的u点走到v点,代价为u到v路径上的权 ...

  8. bzoj 4326: NOIP2015 运输计划(二分+树链剖分)

    传送门 题解: 树链剖分快速求解任意两点间的路径的权值和: 然后,二分答案: 此题的难点是如何快速求解重合路径? 差分数组可以否??? 在此之前先介绍一下相关变量: int fa[maxn]; int ...

  9. 【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

    4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status] ...

随机推荐

  1. 对DataTable进行过滤筛选的一些方法Select,dataview

    当你从数据库里取出一些数据,然后要对数据进行整合,你很容易就会想到: DataTable dt = new DataTable();//假设dt是由"SELECT C1,C2,C3 FROM ...

  2. 【例题 7-8 UVA - 10603】Fill

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 三维显然可以缩短为2维. 只要知道a,b瓶中的水量,c瓶中的水量减一下就能得到. 则设dis[a][b]表示a,b瓶中水量为a,b时 ...

  3. ANSI转UTF-8中文无乱码解决方案

    近期做的项目需要使用Doxygen生成文档,由于前期代码不是本人完成,他使用的是ANSI格式的文件,后来我用Notepad++写其他文件时,默认保存为UTF-8 无BOM编码格式,因此整个项目文件中既 ...

  4. Windows 7 下快速挂载和分离VHD文件的小脚本

    1.保存以下代码为VDM.vbs,放在Windows\system32下 Dim ArgsSet Args = WScript.ArgumentsTranArgs = " "For ...

  5. (转)alter database open resetlogs 的意义

    转自:http://blog.sina.com.cn/s/blog_63216bda0100zblr.html Oracle文档中提到,一旦用备份的控制文件进行数据库恢复,就需要使用resetlogs ...

  6. (转)linux的一个find命令配合rm删除某天前的文件

    转自:http://www.cnblogs.com/mingforyou/p/3930624.html 语句写法:find 对应目录 -mtime +天数 -name "文件名"  ...

  7. jmeter--使用badboy录制脚本

    JMeter录制脚本有多种方法,其中最常见的方法是用第三方工具badboy录制,另外还有JMeter自身设置(Http代理服务器+IE浏览器设置)来录制脚本,但这种方法录制出来的脚本比较多且比较乱,个 ...

  8. Iceberg使用

    Iceberg是Mac下比較好用的pkg生成工具. 在files中选择你想要存放(自己文件的目录),生成pkg后目录就会存储在设置的那个目录下. 点击scripts选择pkg安装各个阶段所要运行脚本路 ...

  9. CNTK 搞深度学习-1

    CNTK 搞深度学习 Computational Network Toolkit (CNTK) 是微软出品的开源深度学习工具包.本文介绍CNTK的基本内容,如何写CNTK的网络定义语言,以及跑通一个简 ...

  10. 自定义npm包的创建、发布、更新和撤销

    大纲 1.准备2.自定义npm包3.发布自定义npm包4.引用npm包5.更新npm包6.撤销发布的npm包 简书原文 https://www.jianshu.com/p/d737bc5df5b7 1 ...