BZOJ 4860: [BeiJing2017]树的难题 点分治+线段树

这个思路还是非常巧妙的. 

困难在于我们需要同时维护以 $x$ 为分治中心,延伸出颜色相同/不同的最大值.      

不同的话直接将权和相加,相同的话还需要减掉重复部分,这就比较难办.  

但是我们发现,当以 $x$ 为分治中心时,$x$ 每一个儿子为根的子树的延伸颜色都是相同的. 

所以我们可以将每一个点的所有儿子按照延伸颜色排序,然后维护两颗线段树:相同与不同. 

当我们在点分治时处理到下一个儿子,而下一个儿子与当前儿子颜色不同时,用线段树合并的方式将相同线段树合并到不同即可. 

code:

#include <cstdio> 
#include <string>  
#include <vector>
#include <cstring> 
#include <algorithm>   
#define N 200006 
#define ll long long  
#define inf 1000000009 
using namespace std; 
void setIO(string s) 
{
    freopen((s+".in").c_str(),"r",stdin);  
    // freopen((s+".out").c_str(),"w",stdout);  
}
int answer=-inf,mp=0; 
namespace seg
{   
    int tot;     
    struct node { int ls,rs,maxx; }s[N*50];      
    void clr()  
    { 
        for(int i=1;i<=tot;++i) s[i].ls=s[i].rs=0,s[i].maxx=-inf;   
        tot=0;  
    }     
    int merge(int x,int y) 
    {                               
        if(!x||!y) return x+y;     
        s[x].maxx=max(s[x].maxx,s[y].maxx);    
        s[x].ls=merge(s[x].ls,s[y].ls); 
        s[x].rs=merge(s[x].rs,s[y].rs);   
        return x;  
    }
    void update(int &x,int l,int r,int p,int v) 
    {
        if(!x) x=++tot,s[x].maxx=-inf;    
        s[x].maxx=max(s[x].maxx,v); 
        if(l==r) return;  
        int mid=(l+r)>>1;   
        if(p<=mid)  update(s[x].ls,l,mid,p,v); 
        else update(s[x].rs,mid+1,r,p,v);   
    }       
    int query(int x,int l,int r,int L,int R) 
    {
        if(!x||L>R) return -inf; 
        if(l>=L&&r<=R) return s[x].maxx;  
        int mid=(l+r)>>1,re=-inf;  
        if(L<=mid) re=max(re,query(s[x].ls,l,mid,L,R)); 
        if(R>mid)  re=max(re,query(s[x].rs,mid+1,r,L,R));     
        return re;   
    }
};  
int n,m,_min,_max,root,sn,tot;       
int val[N],size[N],mx[N],vis[N];     
struct Edge 
{
    int to,w;    
    Edge(int to=0,int w=0):to(to),w(w){}  
};  
struct Dis 
{ 
    int d,v; 
    Dis(int d=0,int v=0):d(d),v(v){}  
}A[N];  
vector<Edge>F[N];  
vector<Edge>G[N];  
bool cmp(Edge a,Edge b) { return a.w<b.w; }   
void getroot(int u,int ff) 
{      
    size[u]=1,mx[u]=0; 
    for(int i=0;i<G[u].size();++i) 
    {
        int v=G[u][i].to;   
        if(vis[v]||v==ff)  continue;       
        getroot(v,u),size[u]+=size[v];   
        mx[u]=max(mx[u],size[v]);                                 
    }
    mx[u]=max(mx[u],sn-size[u]); 
    if(mx[u]<mx[root]) root=u;  
} 
void dfs(int u,int ff,int d,int v,int pr) 
{     
    A[++tot]=Dis(d,v);             
    for(int i=0;i<G[u].size();++i) 
    {
        int y=G[u][i].to; 
        if(vis[y]||y==ff) continue;     
        dfs(y,u,d+1,v+val[G[u][i].w]*(G[u][i].w!=pr),G[u][i].w);                                        
    }
}
void solve(int u) 
{       
    int rt_diff=0,rt_same=0;    
    F[u].clear();  
    for(int i=0;i<G[u].size();++i) if(!vis[G[u][i].to]) F[u].push_back(G[u][i]);   
    for(int i=0;i<F[u].size();++i) 
    {
        int v=F[u][i].to;          
        tot=0; 
        dfs(v,u,1,val[F[u][i].w],F[u][i].w);                       
        for(int j=1;j<=tot;++j) 
        {   
            if(A[j].d>_max)  continue;     
            if(A[j].d>=_min&&A[j].d<=_max) 
            {    
                answer=max(answer,A[j].v),mp=max(mp,A[j].v);      
            }
            answer=max(answer,A[j].v+seg::query(rt_diff,1,n,max(1,_min-A[j].d),_max-A[j].d));       
            answer=max(answer,A[j].v+seg::query(rt_same,1,n,max(1,_min-A[j].d),_max-A[j].d)-val[F[u][i].w]);   
        }
        for(int j=1;j<=tot;++j) seg::update(rt_same,1,n,A[j].d,A[j].v);    
        if(i<F[u].size()-1&&F[u][i+1].w!=F[u][i].w)  
        {
            rt_diff=seg::merge(rt_diff,rt_same); 
            rt_same=0;  
        }                        
    }   
    seg::clr();              
    vis[u]=1;   
    for(int i=0;i<F[u].size();++i) 
    {
        int v=F[u][i].to;     
        sn=size[v],root=0,getroot(v,u),solve(root);  
    }
}
int main() 
{ 
    // setIO("input");  
    int i,j;   
    scanf("%d%d%d%d",&n,&m,&_min,&_max);   
    for(i=1;i<=m;++i)  scanf("%d",&val[i]);     
    for(i=1;i<n;++i) 
    {
        int x,y,z; 
        scanf("%d%d%d",&x,&y,&z);   
        G[x].push_back(Edge(y,z));   
        G[y].push_back(Edge(x,z));  
    }
    for(i=1;i<=n;++i) sort(G[i].begin(),G[i].end(),cmp);                  
    sn=n,mx[root=0]=N,getroot(1,0),solve(root),printf("%d\n",answer); 
    return 0; 
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值