zoukankan      html  css  js  c++  java
  • NOIP2015 运输计划 树上差分(路径覆盖)

    #include<bits/stdc++.h>
    //#pragma comment(linker, "/STACK:1024000000,1024000000") 
    #include<stdio.h>
    #include<algorithm>
    #include<queue>
    #include<string.h>
    #include<iostream>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<iomanip>
    using namespace std;
    #define ll long long
    #define pb push_back
    #define FOR(a) for(int i=1;i<=a;i++)
    
    const int maxn=4e5+9;  
      
    int n,m;
    int cnt;  
    struct EDGE{int v;int d;};  
    vector<EDGE>G[maxn];  
      
    int vs[maxn*2-1];   //每次访问到的节点  
    int dep[maxn*2-1];  //节点深度  
    int id[maxn];       //编号节点第一次访问的时间  
    int tick[maxn];
    int falen[maxn];
    int pre[maxn];
    
    void addedge(int u,int v,int d){  
        G[u].pb((EDGE){v,d});  
        G[v].pb((EDGE){u,d});  
    }  
     
    int dif_clock=0;
    int difvs[maxn];
    void dfs(int u,int fa,int d){  
    	pre[u]=fa;
        difvs[dif_clock++]=u;
    	id[u]=++cnt;  
        vs[cnt]=u;dep[cnt]=d;  
        int sz=G[u].size();  
        for(int i=0;i<sz;i++){  
            EDGE &e=G[u][i];  
            if(e.v==fa)continue; 
    	   	falen[e.v]=e.d;	
            dfs(e.v,u,d+e.d);     
            vs[++cnt]=u;dep[cnt]=d;  
        }  
    }  
      
    int dp[maxn<<1][20];  
    void init_rmq(int n,int dep[]){  
        for(int i=1;i<=n;i++)dp[i][0]=i;  
        for(int j=1;(1<<j)<=n;j++){  
            for(int i=1;i+(1<<j)-1<=n;i++){  
                int a=dp[i][j-1],b=dp[i+(1<<(j-1))][j-1];  
                dp[i][j]=dep[a]<dep[b]?a:b;  
            }  
        }  
    }  
    int rmq(int l,int r){  
        if(l>r)swap(l,r);  
        int ph=0;  
        while((1<<(ph+1)) <= r-l+1)ph++;  
        return dep[dp[l][ph]]<=dep[dp[r-(1<<ph)+1][ph]]?  
            dp[l][ph]:dp[r-(1<<ph)+1][ph];  
    }  
      
    int lca(int u,int v){  
        return vs[rmq(id[u],id[v])];  
    } 
    
    struct LINE{
    	int u,v,lca,len;
    }line[maxn];
    
    bool check(int x){
    	memset(tick,0,sizeof tick);
    	int exc=0,limit=0;
    	for(int i=1;i<=m;i++){
    		if(line[i].len>x){
    			limit=max(limit,line[i].len-x);
    			exc++;
    			tick[line[i].u]++;tick[line[i].v]++;tick[line[i].lca]-=2;
    		}	
    	}
    	if(exc==0)return 1;
    	for(int i=n;i>1;i--){
    		tick[pre[difvs[i]]]+=tick[difvs[i]];	//按dfs序往上更新	
    	}
    	for(int i=2;i<=n;i++){
    		if(falen[i]>=limit && tick[i]==exc)return 1;
    	}
    	return 0;
    }
    
    int Max;
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,u,v,w;i<n;i++){
    		scanf("%d%d%d",&u,&v,&w);
    		addedge(u,v,w);
    		Max+=w;
    	}
    	dfs(1,0,0);
    	init_rmq(n*2-1,dep);
    	for(int i=1,u,v,_lca;i<=m;i++){
    		scanf("%d%d",&u,&v);_lca=lca(u,v);
    		line[i]=(LINE){u,v,_lca,dep[id[u]]+dep[id[v]]-2*dep[id[_lca]]};
    	}
    	int l=1,r=Max,mid,ans;
    	while(l<=r){
    		mid=l+r>>1;
    		if(check(mid)){ans=mid,r=mid-1;}
    		else l=mid+1;
    	}
    	printf("%d
    ",ans);	
    }

    这里的差分主要体现在打标记上

    差分标记一般有两种,

    一种是lca+2:这种标记作用于节点的父边

    另一种是lca+1:这种标记作用于节点

    (差分思想好棒啊

    (啥时候学树剖啊

  • 相关阅读:
    入门菜鸟
    FZU 1202
    XMU 1246
    Codeforces 294E Shaass the Great 树形dp
    Codeforces 773D Perishable Roads 最短路 (看题解)
    Codeforces 814E An unavoidable detour for home dp
    Codeforces 567E President and Roads 最短路 + tarjan求桥
    Codeforces 567F Mausoleum dp
    Codeforces 908G New Year and Original Order 数位dp
    Codeforces 813D Two Melodies dp
  • 原文地址:https://www.cnblogs.com/Drenight/p/8611296.html
Copyright © 2011-2022 走看看