zoukankan      html  css  js  c++  java
  • NOIP2015 D2T3 洛谷2680 BZOJ4326 运输计划 解题报告

    前言:个人认为这是历年NOIP中比较简单的最后一题了,因此将自己的思路与大家分享。

    题目大意:

    给一棵无根树,给出m条路径。允许将树上的一条边的权值改为0。求m条路径长度最大值的最小值。n,m<=300000.

    思考:

    题目将40分部分分给了链的情况。50分的部分分给了n,m<=3000.说明以下两点:

      1.链的情况可能是问题的突破口(类比NOIP2016D1T2的部分分设置)。

      2.可能要从较小的n,m入手(经验之谈:当n,m较小有30分时大多情况是为了分数好看,但n,m较小有50-80分则是启发正解)  

    那么我们将问题简化成一条链。

    对链的情况的分析:

    当树退化成链的时候,我们可以将树用链表保存。为了简单起见,我们用映射数组m将链表中距离链头i个结点的编号映射成i+1。链头的编号为1。这样可以将链表保存在普通的数组里,而不是有nxt域的数组。

    下文用m(i)表示原来第i个结点的映射。

    对于任意询问u,v,必然是从数组的第min(m(v),m(u))个元素到第max(m(u),m(v))个元素。我们可以通过前缀和在O(n)预处理,O(1)的时间获得答案。这样我们得到了未删除边的答案m个。

    现在我们要减少一条边的权值到0。不难发现减少的边一定在得到最大答案的路径上。但不一定是得到最大答案的路径上边权最大的边。原因呢?

    分析答案式:

    最终答案ans=max(w(l1),w(l2),...,w(lm)).w(l)为路径l的长度。当我们减掉的边是最大答案路径上边权最大的边时,答案可能受到次大答案路径的影响。当我们减掉最大答案路径和次大答案路径的交上的最大边时,答案可能受到第三大答案路径的影响。由此分析,答案可能是要减去最大答案路径的最大值,最大答案路径和次大答案路径的交集的最大值,...,所有路径交集的最大值之一。在模拟样例的过程中,我们可以发现答案应该随着上述删除边的变化逐渐变小或不变。若答案变大则可以停止向下查找。

    继续链上的分析:

    网上的题解大多借着答案的单调性以及要求最大值最小,想到了二分答案。在链上时间复杂度为O(m*logmax).

    我比较蠢,没看出来二分答案。我来谈谈我在链上的做法。

    将数组放在线段树上,线段树保存三个data。第一个是区间最大值maxx。第二个是区间被所有路径覆盖的最大值chs,第三个是区间最大覆盖次数mxcv。对于任意的l到r。给l到r这一段的区间最大覆盖次数加一。查找操作时如果mxcv不等于当前枚举的路径数量则直接return。否则返回chs。时间复杂度O(mlogn)。

    放在树上:

    链上的情况解决了,那么树上就很容易了。树链剖分后将问题退化至链上即可解决,时间复杂度需要加一个log,为O(mlognlogn)。

    但是不知道为什么我的用时比树上前缀和的O(nlogmaxn)要快

    代码:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 const int maxn = 300003;
      4 struct edge{int to,w,nxt;}e[maxn<<1],get_LCA[maxn<<1];
      5 struct node{int mxcv,maxx,chs,lazy;}t[maxn<<2];
      6 struct ask{int from,to,LCA,len;}Pro[maxn],*P=Pro;
      7 int n,m,NM,NM2,g[maxn],top[maxn],fa[maxn],sz[maxn],son[maxn],ABOUT_LCA[maxn];
      8 int pre[maxn],dep[maxn],call[maxn],abcall[maxn],tms[maxn],FW[maxn],stnd=1,l,r;
      9 int found(int x){
     10     int rx = x; while(pre[rx]!=rx)rx=pre[rx];
     11     while(pre[x]!=rx){int t=pre[x];pre[x]=rx;x=t;}
     12     return rx;
     13 }
     14 void dfs1(int now,int Prt,int DPTH){//Prt=parent                 //as we get fa,size,depth,Father_road_weight,we can also get LCA
     15     fa[now] = Prt;dep[now] = DPTH;int maxx = 0;
     16     for(int i=ABOUT_LCA[now];i;i=get_LCA[i].nxt){
     17     if(!fa[get_LCA[i].to])continue;
     18     Pro[get_LCA[i].w].LCA = found(get_LCA[i].to);
     19     }
     20     for(int i=g[now];i;i=e[i].nxt){
     21     if(e[i].to==Prt)continue;
     22     dfs1(e[i].to,now,DPTH+e[i].w);
     23     sz[now]+=sz[e[i].to];FW[e[i].to]=e[i].w;
     24     if(sz[maxx]<sz[e[i].to])maxx=e[i].to;
     25     }
     26     sz[now]++;son[now] = maxx;
     27     pre[found(now)] = found(Prt);
     28 }
     29 void dfs2(int now,int tp,int Val){
     30     if(tp != now) NM++,call[now] = NM,abcall[NM]=Val;
     31     top[now] = tp;
     32     if(son[now]) dfs2(son[now],tp,FW[son[now]]);
     33     for(int i=g[now];i;i=e[i].nxt){
     34     if(e[i].to == son[now] || e[i].to == fa[now])continue;
     35     dfs2(e[i].to,e[i].to,e[i].w);
     36     }
     37 }
     38 void build_tree(int l,int r,int now){
     39     if(l == r) t[now].chs=t[now].maxx=abcall[l];
     40     else{
     41     int mid = (l+r)/2;
     42     build_tree(l,mid,now<<1),build_tree(mid+1,r,now<<1|1);
     43     t[now].maxx = t[now].chs = max(t[now<<1].maxx,t[now<<1|1].maxx);
     44     }
     45 }
     46 void add_edge(int a,int b,int v,int NUM[],int &u,edge NE[]){
     47     NE[++u] = (edge){b,v,NUM[a]}; NUM[a] = u;
     48     NE[++u] = (edge){a,v,NUM[b]}; NUM[b] = u;
     49 }
     50 void read(){
     51     scanf("%d%d",&n,&m);
     52     for(int i=1,a,b,v;i<n;i++){scanf("%d%d%d",&a,&b,&v);add_edge(a,b,v,g,NM,e);}
     53     for(int i=1;i<=m;i++){scanf("%d%d",&Pro[i].from,&Pro[i].to);}
     54     for(int i=1;i<=m;i++){add_edge(Pro[i].from,Pro[i].to,i,ABOUT_LCA,NM2,get_LCA);}
     55 }
     56 void Heavy_Lt_Dec(){
     57     for(int i=1;i<=n;i++) pre[i] = i;
     58     dfs1(1,-1,0); NM=0; dfs2(1,1,0); build_tree(1,NM,1);
     59 }
     60 void push_up(int now){
     61     t[now].mxcv = max(t[now<<1].mxcv,t[now<<1|1].mxcv);
     62     if(t[now<<1].mxcv > t[now<<1|1].mxcv)t[now].chs=t[now<<1].chs;
     63     else if(t[now<<1].mxcv < t[now<<1|1].mxcv)t[now].chs=t[now<<1|1].chs;
     64     else t[now].chs = max(t[now<<1].chs,t[now<<1|1].chs);
     65 }
     66 void push_down(int now){
     67     t[now<<1].lazy+=t[now].lazy;t[now<<1|1].lazy+=t[now].lazy;
     68     t[now<<1].mxcv+=t[now].lazy;t[now<<1|1].mxcv+=t[now].lazy;
     69     t[now].lazy=0;
     70 }
     71 int update(int tl,int tr,int now){
     72     if(t[now].mxcv < stnd-1 || l>tr || r<tl)return 0;
     73     if(tl >= l && tr <= r){t[now].mxcv++;t[now].lazy++;return t[now].chs;}
     74     if(t[now].lazy) push_down(now);
     75     int mid=(tl+tr)/2,ans=max(update(tl,mid,now*2),update(mid+1,tr,now*2+1));
     76     push_up(now);
     77     return ans;
     78 }
     79 int ADD_Tree(int OP,int ED,int PB){
     80     int ans = 0;
     81     while(OP != PB){
     82     l=call[son[top[OP]]],r=call[OP];int kkk = top[OP];
     83     if(top[OP]==top[PB])l=call[son[PB]],kkk=PB;
     84     if(top[OP]==OP){if(++tms[OP]==stnd)ans=max(ans,FW[OP]);OP=fa[OP];}
     85     else{ans=max(ans,update(1,NM,1));OP=kkk;}
     86     }
     87     while(ED != PB){
     88     l=call[son[top[ED]]],r=call[ED];int kkk = top[ED];
     89     if(top[ED]==top[PB])l=call[son[PB]],kkk=PB;
     90     if(top[ED]==ED){if(++tms[ED]==stnd)ans=max(ans,FW[ED]);ED=fa[ED];}
     91     else{ans=max(ans,update(1,NM,1));ED=kkk;}
     92     }
     93     return ans;
     94 }
     95 int cmp(ask a,ask b){return a.len<b.len;}
     96 void work(){
     97     for(int i=1;i<=m;i++)Pro[i].len=dep[P[i].from]+dep[P[i].to]-2*dep[P[i].LCA];
     98     sort(Pro+1,Pro+m+1,cmp);int maxans = Pro[m].len;
     99     for(int i=m;i>=1;i--,stnd++){
    100     int ans = max(P[m].len-ADD_Tree(P[i].from,P[i].to,P[i].LCA),P[i-1].len);
    101     if(ans > maxans)break; else maxans = ans;
    102     }
    103     printf("%d",maxans);
    104 }
    105 int main(){read();Heavy_Lt_Dec();work();return 0;}
  • 相关阅读:
    用C++读写EXCEL文件的几种方式比较
    20个值得收藏的网页设计开放课件
    char* 应用, 去除字符串内多余空格, 用算法而非库函数
    东拉西扯:王建硕主义
    Lisp 的本质(The Nature of Lisp)
    web前端:html
    [原译]理解并实现原型模式实现ICloneable接口.理解深浅拷贝
    [原译]理解并实现装饰器模式
    3分钟理解Lambda表达式
    [原译]实现IEnumerable接口&理解yield关键字
  • 原文地址:https://www.cnblogs.com/1-1-1-1/p/7675753.html
Copyright © 2011-2022 走看看