[NOIP2015提高组]运输计划
题目:BZOJ4326、洛谷P2680、Vijos P1983、UOJ#150、codevs4632、codevs5440。
题目大意:有一棵带权树,有一些运输计划,第i个运输计划从ai到bi,耗时为ai到bi的距离,所有运输计划一起开始。现在可以把一条边权变成0,求最终运输计划最短要多少时间。
解题思路:标算是树剖,然而我并不会……
我的方法是LCA+二分+树上差分。
首先LCA,求出每个运输计划的长度,可一遍dfs求出每个节点到根的距离dist,则a到b的长度为dist[a]+dist[b]-(dist[lca(a,b)]<<1)。
接着二分答案,然后判断答案可行性。
对于每一个答案,我们要找的是所有长度大于当前答案的运输计划的公共边,因为只有所有长度大于当前答案的运输计划全部变成小于等于当前答案,当前答案才可行。
用树上差分(不懂请百度)。我们用s[i]记录i到它父亲这条边有多少计划经过。
对于每个运输计划,如果长度大于当前答案,我们给s[a]+1,s[b]+1,s[lca(a,b)]-2,因为我们要统计的是边,所以对于两个点,lca(a,b)对应的边实际是没有走到的,所以-2。
差分完后判断答案可行性即可。
我用倍增时间复杂度为$O(m\log_2 n+(n+m)\log_2 len)$,len为运输计划的最大长度。
但常数巨大,1s很容易被卡,因此有些地方还过不掉。例如我在UOJ上就被卡97。但在时限较宽或数据较弱的地方还是能AC的。
C++ Code:
- %:pragma GCC optimize(2)
- #include<cstdio>
- #include<cctype>
- #include<cstring>
- using namespace std;
- char buf[10700005];
- int bufpos,n,m,head[300001],deep[300001],p[300001][21],dist[300001],s[300001],fa_edge[300001],mx,now;
- struct edge{
- int to,dis,nxt;
- }e[600003];
- struct que{
- int a,b,len,LCA;
- }f[300001];
- inline int max(int a,int b){return(a>b)?a:b;}
- inline int readint(){
- char c=buf[bufpos++];
- for(;!isdigit(c);c=buf[bufpos++]);
- int p=0;
- for(;isdigit(c);c=buf[bufpos++])p=(p<<3)+(p<<1)+(c^'0');
- return p;
- }
- void dfs(int s){
- for(int i=head[s];i;i=e[i].nxt)
- if(!deep[e[i].to]){
- fa_edge[e[i].to]=i;
- deep[e[i].to]=deep[s]+1;
- p[e[i].to][0]=s;
- dist[e[i].to]=dist[s]+e[i].dis;
- dfs(e[i].to);
- }
- }
- int lca(int x,int y){
- if(deep[x]<deep[y])x^=y^=x^=y;
- int i;
- for(i=0;(1<<i)<=deep[x];++i);--i;
- for(int j=i;j>-1;--j)
- if(deep[p[x][j]]>=deep[y])x=p[x][j];
- if(x==y)return x;
- for(int j=i;j>-1;--j)
- if(p[x][j]!=-1&&p[x][j]!=p[y][j])x=p[x][j],y=p[y][j];
- return p[x][0];
- }
- void updata(int now){
- for(int i=head[now];i;i=e[i].nxt)
- if(deep[now]<deep[e[i].to]){
- updata(e[i].to);
- s[now]+=s[e[i].to];
- }
- }
- bool ok(int ans){
- memset(s,0,sizeof s);
- int gz=0;
- for(int i=1;i<=n;++i)
- if(f[i].len>ans){
- ++gz;
- ++s[f[i].a];
- ++s[f[i].b];
- s[f[i].LCA]-=2;
- }
- updata(1);
- for(int i=1;i<=m;++i)
- if(s[i]==gz&&mx-ans<=e[fa_edge[i]].dis)return true;
- return false;
- }
- int main(){
- buf[fread(buf,1,10700000,stdin)]=bufpos=0;
- m=readint(),n=readint();
- for(int i=1;i<m;++i){
- int from=readint(),to=readint(),dis=readint();
- now=i<<1;
- e[now-1]=(edge){to,dis,head[from]};
- head[from]=now-1;
- e[now]=(edge){from,dis,head[to]};
- head[to]=now;
- }
- memset(p,-1,sizeof p);
- dist[1]=0;
- deep[1]=1;
- dfs(1);
- for(int j=1;(1<<j)<=m;++j)
- for(int i=1;i<=m;++i)
- if(p[i][j-1]!=-1)p[i][j]=p[p[i][j-1]][j-1];
- int l=0,r=0,mid;
- for(int i=1;i<=n;++i){
- f[i].a=readint();
- f[i].b=readint();
- f[i].LCA=lca(f[i].a,f[i].b);
- r=max(r,f[i].len=dist[f[i].a]+dist[f[i].b]-(dist[f[i].LCA]<<1));
- }
- mx=r;
- ++r;
- while(l<r){
- mid=l+r>>1;
- if(ok(mid))r=mid;else
- l=mid+1;
- }
- printf("%d\n",r);
- return 0;
- }
倍增时间复杂度比较高,我改成用Tarjan求LCA,速度瞬间变快,在UOJ上成功卡了过去。不过codevs4632仍然被卡。
以下为Tarjan代码。
C++ Code:
- %:pragma GCC optimize(2)
- #include<cstdio>
- #include<cctype>
- #include<cstring>
- using namespace std;
- char buf[10700005];
- int bufpos,n,m,head[300001],deep[300001],dist[300001],s[300001],fa_edge[300001],mx,now,headq[300001],nq=0;
- bool vis[300001];
- int fa[300001];
- struct edge{
- int to,dis,nxt;
- }e[600003];
- struct que{
- int a,b,len,LCA;
- }f[300001];
- struct Query{
- int same,nxt,to,num;
- bool flag;
- }q[600003];
- int find(int x){return(fa[x]==x)?x:(fa[x]=find(fa[x]));}
- inline int max(int a,int b){return(a>b)?a:b;}
- inline int readint(){
- char c=buf[bufpos++];
- for(;!isdigit(c);c=buf[bufpos++]);
- int p=0;
- for(;isdigit(c);c=buf[bufpos++])p=(p<<3)+(p<<1)+(c^'0');
- return p;
- }
- void dfs(int s){
- for(int i=head[s];i;i=e[i].nxt)
- if(!deep[e[i].to]){
- fa_edge[e[i].to]=i;
- deep[e[i].to]=deep[s]+1;
- dist[e[i].to]=dist[s]+e[i].dis;
- dfs(e[i].to);
- }
- }
- void tarjan(int root){
- for(int i=head[root];i;i=e[i].nxt)
- if(deep[root]<deep[e[i].to]){
- tarjan(e[i].to);
- fa[e[i].to]=root;
- vis[e[i].to]=true;
- }
- for(int i=headq[root];i;i=q[i].nxt)
- if(vis[q[i].to]&&!q[i].flag){
- q[i].flag=q[q[i].same].flag=true;
- f[q[i].num].LCA=find(q[i].to);
- }
- }
- void updata(int now){
- for(int i=head[now];i;i=e[i].nxt)
- if(deep[now]<deep[e[i].to]){
- updata(e[i].to);
- s[now]+=s[e[i].to];
- }
- }
- bool ok(int ans){
- memset(s,0,sizeof s);
- int gz=0;
- for(int i=1;i<=n;++i)
- if(f[i].len>ans){
- ++gz;
- ++s[f[i].a];
- ++s[f[i].b];
- s[f[i].LCA]-=2;
- }
- updata(1);
- for(int i=1;i<=m;++i)
- if(s[i]==gz&&mx-ans<=e[fa_edge[i]].dis)return true;
- return false;
- }
- int main(){
- buf[fread(buf,1,10700000,stdin)]=bufpos=0;
- m=readint(),n=readint();
- for(int i=1;i<m;++i){
- int from=readint(),to=readint(),dis=readint();
- now=i<<1;
- e[now-1]=(edge){to,dis,head[from]};
- head[from]=now-1;
- e[now]=(edge){from,dis,head[to]};
- head[to]=now;
- fa[i]=i;
- }
- fa[m]=m;
- deep[1]=1;
- dfs(1);
- int l=0,r=0,mid;
- for(int i=1;i<=n;++i){
- f[i].a=readint();
- f[i].b=readint();
- int& x=f[i].a,&y=f[i].b;
- q[++nq].to=y;
- q[nq].same=nq+1;
- q[nq].num=i;
- q[nq].nxt=headq[x];
- headq[x]=nq;
- q[++nq].to=x;
- q[nq].same=nq-1;
- q[nq].num=i;
- q[nq].nxt=headq[y];
- headq[y]=nq;
- if(x==y)
- q[nq].flag=q[nq-1].flag=true,f[i].LCA=x;
- }
- tarjan(1);
- for(int i=1;i<=n;++i)
- r=max(r,f[i].len=dist[f[i].a]+dist[f[i].b]-(dist[f[i].LCA]<<1));
- mx=r;
- ++r;
- while(l<r){
- mid=l+r>>1;
- if(ok(mid))r=mid;else
- l=mid+1;
- }
- printf("%d\n",r);
- return 0;
- }
[NOIP2015提高组]运输计划的更多相关文章
- NOIP2015 提高组] 运输计划
码农题啊兄弟们. 随便考虑二分一下,然后发现要取一条满足性质的边. 被所有大于\(mid\)的路径都覆盖,取了之后能把他们都弄到小于\(mid\) 那就树上差分再处理一下. 写了\(180h\),老年 ...
- [NOIP2015 提高组] 运输计划题解
题目链接:P2680 [NOIP2015 提高组] 运输计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 看了好长时间题解才终于懂的,有关lca和二分答案的题解解释的不详细,一时 ...
- 洛谷 P2680 [NOIP2015 提高组] 运输计划
链接:P2680 题意: 在树上把一条边边权变为0使得最长给定路径最短 分析: 最大值最小可以想到二分答案,对于每一个mid,寻找所有大于mid的路径,再寻找是否存在一条边使得删去它后大于mid的路径 ...
- P2680 [NOIP2015 提高组] 运输计划 (树上差分-边差分)
P2680 题目的大意就是走完m条路径所需要的最短时间(边权是时间), 其中我们可以把一条边的权值变成0(也就是题目所说的虫洞). 可以考虑二分答案x,找到一条边,使得所有大于x的路径都经过这条边(差 ...
- 【数据结构】运输计划 NOIP2015提高组D2T3
[数据结构]运输计划 NOIP2015提高组D2T3 >>>>题目 [题目描述] 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航 ...
- 【题解】NOIP2015提高组 复赛
[题解]NOIP2015提高组 复赛 传送门: 神奇的幻方 \([P2615]\) 信息传递 \([P2661]\) 斗地主 \([P2668]\) 跳石头 \([P2678]\) 子串 \([P26 ...
- 洛谷 P2678 & [NOIP2015提高组] 跳石头
题目链接 https://www.luogu.org/problemnew/show/P2678 题目背景 一年一度的“跳石头”比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布 ...
- 【二分查找】 跳石头NOIP2015提高组 D2T1
[二分查找]跳石头NOIP2015提高组 D2T1 >>>>题目 [题目描述] 一年一度的“跳石头”比赛又要开始了! 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石 ...
- [NOIP2015] 提高组 洛谷P2615 神奇的幻方
题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. ...
随机推荐
- solarwinds之网络发现
1. 首先需要添加网络发现 2. 使用public 3. 添加主机 4. 网络地址选择 5. 默认下一步 6. 运行发现 7. 扫描结构如下 8. 下一步 ...
- hiho 1620 - 股票价格3 - 无限制的单调队列?
题目链接 小Hi最近在关注股票,为了计算股票可能的盈利,他获取了一只股票最近N天的价格A1~AN. 小Hi想知道,对于第i天的股票价格Ai,几天之后股价会第一次超过Ai. 假设A=[69, 73, 6 ...
- Caffe 激励层(Activation)分析
Caffe_Activation 一般来说,激励层的输入输出尺寸一致,为非线性函数,完成非线性映射,从而能够拟合更为复杂的函数表达式激励层都派生于NeuronLayer: class XXXlayer ...
- UVA-1347 Tour 动态规划 难以确定的状态
题目链接:https://cn.vjudge.net/problem/UVA-1347 题意 给出按x坐标排序的几个点. 欲从最左边不回头的走到最右边,然后再返回最左边. 每个点都要被访问,且只能经过 ...
- HISTFILESIZE与HISTSIZE的区别
在linux系统中,history命令可以输出历史命令,历史命令默认保存在文件~/.bash_history中. HISTFILESIZE 与 HISTSIZE都是history命令需要用到的两个sh ...
- 2015 Multi-University Training Contest 1 OO’s Sequence
OO’s Sequence Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)T ...
- PlayFramework的安装和配置以及向eclipse导入项目工程
一.Play的安装和配置 1.首先去官网下载Play的包并将其解压 我下的是playframework2.2.1 2.配置play的环境变量方便使用 3.打开cmd运行play 输入play he ...
- LeetCode——Longest Common Prefix
Write a function to find the longest common prefix string amongst an array of strings. 写一个函数找出字符串数组中 ...
- Android中加入思源字体/NotoSansCJK/SourceHanSans
系统版本号:Android 4.2.2_r1 本文主要是在Android中加入思源字体的过程记录. 思源字体是Google和Adobe在2014.07.18公布的中文字体. 1.获取思源字体(Goog ...
- 死锁的Dump文件
死锁的Dump文件 package com.stono.thread; public class DeadLockDemo { private static String A = "A&qu ...