BZOJ4326: NOIP2015 运输计划

ゝ一世哀愁。 2021-12-23 06:01 390阅读 0赞

题目大意:给出一棵带边权的树和m条路径,可以将一条边的边权变成0,求问最长的路径最短是多少。

题解:

暴力算法:将每条边变不变,用数据结构维护,更新答案。

这样显然过不掉。

通常最值问题考虑贪心和二分答案,这里我们使用二分答案,二分最长的路径是多少。

假如最长路径maxn<=当前二分的值mid,那么结果显然可行。

如果maxn-mid可以通过将一条边的边权变成0来消去,即这条边的边权>=maxn-mid,那么结果也可行。

现在问题转化成:路径长度>mid的所有路径中,是否存在一条公共边,满足这条公共边的边权>=maxn-mid。

如何判断公共边呢?我们记一下每条边经过的次数,如果经过次数等于路径数,则说明这条边是公共边。

经过次数用树上查分来维护就好。

可是你会发现你会被卡常。。。

在这里给出几个优化的方法:

1.快速读入。

2.缩小l和r的范围,由于只能将一条边的边权变成0,所以左边界是最长路径maxn-最大边权tmp,右边界就是最长路径maxn。

3.有很多时间是花费在求LCA上,预处理出每条路径两个端点的LCA。

当然还有更毒瘤的优化方式,这里就不一一赘述了。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<string>
  6. #include<map>
  7. #include<iostream>
  8. #include<queue>
  9. using namespace std;
  10. #define isNum(a) (a>='0'&&a<='9')
  11. #define SP putchar(' ')
  12. #define EL putchar('\n')
  13. #define File(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
  14. template<class T1>void read(T1 &r_e_a_d);
  15. template<class T1>void write(T1 w_r_i_t_e);
  16. int n,m,len,x,y,z,from[300005],to[300005],head[300005];
  17. struct EDGE{
  18. int to,next,num;
  19. }edge[600005];
  20. void add(int u,int v,int w){
  21. ++len;
  22. edge[len].to=v;
  23. edge[len].next=head[u];
  24. edge[len].num=w;
  25. head[u]=len;
  26. }
  27. int fa[300005],dep[300005],son[300005],siz[300005],dist[300005];
  28. void dfs1(int u,int father){
  29. fa[u]=father;dep[u]=dep[father]+1;siz[u]=1;
  30. for (register int i=head[u];i;i=edge[i].next){
  31. int v=edge[i].to;
  32. if (v!=father){
  33. dist[v]=dist[u]+edge[i].num;
  34. dfs1(v,u);
  35. siz[u]+=siz[v];
  36. if (son[u]==-1||siz[v]>siz[son[u]]) son[u]=v;
  37. }
  38. }
  39. }
  40. int top[300005];
  41. void dfs2(int u,int tp){
  42. top[u]=tp;
  43. if (son[u]==-1) return ;
  44. dfs2(son[u],tp);
  45. for (register int i=head[u];i;i=edge[i].next){
  46. int v=edge[i].to;
  47. if (v!=fa[u]&&v!=son[u]){
  48. dfs2(v,v);
  49. }
  50. }
  51. }
  52. int LCA(int u,int v){
  53. while (top[u]!=top[v]){
  54. if (dep[top[u]]>dep[top[v]]) u=fa[top[u]];
  55. else v=fa[top[v]];
  56. }
  57. if (dep[u]<dep[v]) return u;
  58. return v;
  59. }//树链剖分求LCA
  60. int tmp,maxn,l,r,mid,ans,cnt[300005],e,lca[300005],dis[300005],dif[300005];
  61. void prepare(int u,int father){
  62. dif[u]=cnt[u];
  63. for (register int i=head[u];i;i=edge[i].next){
  64. int v=edge[i].to;
  65. if (v!=father){
  66. prepare(v,u);
  67. dif[u]+=dif[v];
  68. }
  69. }
  70. }
  71. bool dfs(int u,int father,int k,int ed){
  72. for (register int i=head[u];i;i=edge[i].next){
  73. int v=edge[i].to;
  74. if (v!=father){
  75. if (dif[v]==ed&&edge[i].num>=k) return 1;
  76. if (dfs(v,u,k,ed)) return 1;
  77. }
  78. }
  79. return 0;
  80. }
  81. bool check(int k){
  82. memset (dif,0,sizeof (dif));
  83. memset (cnt,0,sizeof (cnt));
  84. e=0;
  85. if (maxn<=k) return 1;
  86. for (register int i=1;i<=m;++i){
  87. if (dis[i]>k){
  88. ++e;
  89. ++cnt[from[i]];++cnt[to[i]];cnt[lca[i]]-=2;
  90. }
  91. }
  92. prepare(1,0);
  93. return dfs(1,0,maxn-k,e);
  94. }//树上差分代码
  95. int main(){
  96. memset (son,-1,sizeof (son));
  97. read(n);read(m);
  98. for (register int i=1;i<n;++i){
  99. read(x);read(y);read(z);
  100. add(x,y,z);add(y,x,z);
  101. tmp=max(tmp,z);
  102. }
  103. dfs1(1,0);dfs2(1,1);
  104. for (register int i=1;i<=m;++i){
  105. read(from[i]);read(to[i]);
  106. lca[i]=LCA(from[i],to[i]);
  107. dis[i]=dist[from[i]]+dist[to[i]]-dist[lca[i]]*2;
  108. r=max(r,dis[i]);
  109. }
  110. maxn=r;l=r-tmp;
  111. while (l<=r){
  112. mid=l+r>>1;
  113. if (check(mid)) ans=mid,r=mid-1;
  114. else l=mid+1;
  115. }
  116. write(ans);EL;
  117. return 0;
  118. }
  119. template<class T1>void read(T1 &r_e_a_d){
  120. T1 k=0;
  121. char ch=getchar();
  122. int flag=1;
  123. while(!isNum(ch)){
  124. if(ch=='-'){
  125. flag=-1;
  126. }
  127. ch=getchar();
  128. }
  129. while(isNum(ch)){
  130. k=((k<<1)+(k<<3)+ch-'0');
  131. ch=getchar();
  132. }
  133. r_e_a_d=flag*k;
  134. }
  135. template<class T1>void write(T1 w_r_i_t_e){
  136. if(w_r_i_t_e<0){
  137. putchar('-');
  138. write(-w_r_i_t_e);
  139. }else{
  140. if(w_r_i_t_e<10){
  141. putchar(w_r_i_t_e+'0');
  142. }else{
  143. write(w_r_i_t_e/10);
  144. putchar((w_r_i_t_e%10)+'0');
  145. }
  146. }
  147. }

  

转载于:https://www.cnblogs.com/DFTMR/p/10756238.html

发表评论

表情:
评论列表 (有 0 条评论,390人围观)

还没有评论,来说两句吧...

相关阅读

    相关 [NOIP2015]跳石头

    题目: [NOIP2015]跳石头 ,哈哈,我们今天来看一道二分答案的题嘛,这是选自NOIP上的一道题,好了,我们一起来看看题意吧:题目描述是复制的,可能有部分显示不...

    相关 [NOIP2015]金币

    链接:https://ac.nowcoder.com/acm/problem/16490 来源:牛客网 题目描述 国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收

    相关 BZOJ1003: [ZJOI2006]物流运输

    [题目链接][Link 1] 发现如果没有限制的话,就是最短路的模板题。但是这道题的关键就是要处理题上的限制。我们就可以用一个数组来存哪一天哪个港口不能走,跑最短路的时候特判

    相关 2015暑假计划

    我习惯在放假前制定些计划,是从上个寒假开始的,对于上个寒假的完成情况我给自己打上85分。把计划发布在自己的博客上一方面来提醒自己,另一方面如果能被朋友借鉴,能够对大家有所帮助,

    相关 BZOJ4326: NOIP2015 运输计划

    题目大意:给出一棵带边权的树和m条路径,可以将一条边的边权变成0,求问最长的路径最短是多少。 题解: 暴力算法:将每条边变不变,用数据结构维护,更新答案。 这样显然过不掉

    相关 运输计划

    [传送门][Link 1] 解法: 首先要学会求 树的最近公共祖先(LCA) 没用树剖 用了一个经常可以代替树剖的方法 树上差分 这个方法很优秀 一定要掌