博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[NOIP2015提高组]运输计划
阅读量:5317 次
发布时间:2019-06-14

本文共 3422 字,大约阅读时间需要 11 分钟。

题目: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
#include
#include
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]
-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]
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
>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
#include
#include
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]
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
>1; if(ok(mid))r=mid;else l=mid+1; } printf("%d\n",r); return 0;}

 

转载于:https://www.cnblogs.com/Mrsrz/p/7635580.html

你可能感兴趣的文章
web前端面试题2017
查看>>
ELMAH——可插拔错误日志工具
查看>>
MySQL学习笔记(四)
查看>>
【Crash Course Psychology】2. Research & Experimentation笔记
查看>>
两数和
查看>>
移动设备和SharePoint 2013 - 第3部分:推送通知
查看>>
SOPC Builder中SystemID
查看>>
MySQL数据库备份工具mysqldump的使用(转)
查看>>
NTP服务器配置
查看>>
【转】OO无双的blocking/non-blocking执行时刻
查看>>
ul li剧中对齐
查看>>
关于 linux 的 limit 的设置
查看>>
HDU(4528),BFS,2013腾讯编程马拉松初赛第五场(3月25日)
查看>>
vim中文帮助教程
查看>>
MySQL基础3
查看>>
云计算数据与信息安全防护
查看>>
RxJS & Angular
查看>>
面向对象(多异常的声明与处理)
查看>>
MTK笔记
查看>>
ERROR: duplicate key value violates unique constraint "xxx"
查看>>