[NOIP2015]运输计划 树链剖分 二分 差分

来源:互联网 发布:可以修改图片的软件 编辑:程序博客网 时间:2024/05/20 04:50

运输计划

  • 关键字:树链剖分,二分,差分

结果和评价

  • 得分:70
  • 时间
  • 评价:不应该

我的思路

  • 暴力直接60
  • 直接否定了二分,然后瞎搞,多水了10,没有什么思考。。。

正确思路

  • 求最长路径最短,应该要先考虑考虑二分。
  • 二分一个答案之后,比答案小的rout都无需考虑了
    • 然后我们考虑一下要删什么边,显然要删边一定在到剩下rout的交集里。
    • 而且一定要满足可以从中取出一条边满足MxroutMxedge<=limt

讲道理这个思路是非常顺的。

怎么说呢,可以思考一下只有一条边的情况,我们要删的边一定是路径上最长的边。

也就是说,我们要删的边是使最后路径全部合法的边。是可以确定的。

  • 然后我们发现现在有个问题需要解决
    • 高效得覆盖路径
    • 高效查询出所以路径交集中的最大边
  • 怎么覆盖呢。。。
  • 先看看链的情况。对于一个路径s,t,我们可以用一个cov数组记录每个点被覆盖了几次。
  • 然后把cov[s+1,t](我们用标记子节点的方式标记边)都加1。。。复杂度为O(nm)的,没有什么屁用。
  • 发现了吧,完全可以用差分数组来实现这一覆盖的过程,复杂度为O(n+m)
  • 这样就轻松地拿到了80分
  • 然后考虑一下树的情况。。。
  • 额,树上的路径覆盖。。。链上的路径覆盖。。。好像树剖一下就没有什么区别了。。。
  • 每次,直接树剖一下,就可以了。
  • 复杂度为(log(nt)(log(n)m+n)
  • 由于树剖的常数是比较优的,所以可以轻松跑过。。

实现

#include<stdio.h>#include<string.h>#include<math.h>#include<ctype.h>#include<time.h>#include<algorithm>#include<iostream>#include<vector>#include<queue>#include<map>#include<set>#include<bitset>#include<stack>#include<string>using namespace std;#define bug(x) cerr<<#x<<'='<<x<<' '#define debug(x) cerr<<#x<<'='<<x<<'\n'#define For(i,a,b) for(register int i=a;i<=b;++i)#define Ror(i,a,b) for(register int i=b;i>=a;--i)typedef long long ll;template<class T>void rd(T&x){    x=0;char c,f=1;    while(c=getchar(),!isdigit(c))if(c=='-')f=-1;    do x=(x<<1)+(x<<3)+(c^'0');    while(c=getchar(),isdigit(c));    x*=f;}template<class T>void pf(T x){    static int top=0,stk[1000];    if(!x)putchar('0');    if(x<0)putchar('-'),x=-x;    while(x)stk[++top]=x%10,x/=10;    while(top)putchar(stk[top--]+'0');}template<class T>void pt(T x){pf(x),putchar(' ');}template<class T>void ptn(T x){pf(x),putchar('\n');}template<class T>void Max(T&x,T y){if(x<y)x=y;}template<class T>void Min(T&x,T y){if(y<x)x=y;}const int M=3e5+5,INF=1e9;int fa[M],tp[M],son[M],sz[M],dep[M],n,m,dis[M],to[M];struct node{int s,t,v;}rout[M];struct E{int t,id,nxt;}G[M<<1];int h[M],E_tot,cost[M],mp[M],Dfn,dfn[M];void E_Add(int x,int y,int id){    G[++E_tot]=(E){x,id,h[y]};h[y]=E_tot;    G[++E_tot]=(E){y,id,h[x]};h[x]=E_tot;}void dfs(int x,int f,int d){    dis[x]=d;    fa[x]=f;sz[x]=1;    dep[x]=dep[f]+1;    for(int i=h[x];i;i=G[i].nxt){        int y=G[i].t,c=cost[G[i].id];        if(y==f)continue;        to[y]=G[i].id;        dfs(y,x,d+c);        sz[x]+=sz[y];        if(sz[son[x]]<sz[y])son[x]=y;    }}void rfs(int x){    mp[dfn[x]=++Dfn]=x;    if(son[x]){        tp[son[x]]=tp[x];        rfs(son[x]);    }    for(int i=h[x];i;i=G[i].nxt){        int y=G[i].t;        if(y==fa[x]||y==son[x])continue;        rfs(tp[y]=y);    }}void init(){    dfs(1,0,0);    rfs(tp[1]=1);}int LCA(int x,int y){    while(tp[x]!=tp[y]){        if(dep[tp[x]]<dep[tp[y]])swap(x,y);        x=fa[tp[x]];    }    if(dep[y]<dep[x])return y;    return x;}int calc(node x){    int s=x.s,t=x.t,v=LCA(s,t);    return dis[s]+dis[t]-2*dis[v];}int Add[M];void insert(int l,int r){    ++Add[l],--Add[r+1];}void EAdd(int x,int y){    while(tp[x]!=tp[y]){        if(dep[tp[x]]<dep[tp[y]])swap(x,y);        int l=dfn[tp[x]],r=dfn[x];        insert(l,r);        x=fa[tp[x]];    }    if(x!=y){        int l=dfn[x],r=dfn[y];        if(l>r)swap(l,r);        insert(l+1,r);    }}bool check(int limt){    int cnt=0,Mxr=0,Mxe=0;    memset(Add,0,sizeof(Add));    For(i,1,m)if(limt<rout[i].v){        ++cnt;        EAdd(rout[i].s,rout[i].t);        if(Mxr<rout[i].v)Mxr=rout[i].v;    }    int cov=0;    For(i,1,n){        cov+=Add[i];        if(cov==cnt){            int d=cost[to[mp[i]]];            if(Mxe<d)Mxe=d;        }    }    if(Mxr-Mxe<=limt)return 1;    return 0;}int main(){    rd(n),rd(m);    For(i,1,n-1){        int a,b;        rd(a),rd(b),rd(cost[i]);        E_Add(a,b,i);    }    init();    For(i,1,m){        rd(rout[i].s);        rd(rout[i].t);        rout[i].v=calc(rout[i]);    }    int l=0,r=3e8,res=r;    while(l<=r){        int mid=(l+r)>>1;        if(check(mid))res=mid,r=mid-1;        else l=mid+1;    }    ptn(res);    return 0;}
阅读全文
1 0
原创粉丝点击