【JZOJ5077】【GDOI2017第三轮模拟day2】树的难题

55 篇文章 0 订阅
30 篇文章 0 订阅

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

这还是一道树分治的题。我们觉得相同颜色很麻烦,所以我们可以在当前的分治重心中先把与重心相连的边按颜色排个序,然后处理出每棵树到根路径上的颜色权值。维护两颗线段树,一颗表示与当前走的儿子颜色不同的答案,一颗表示颜色相同的答案。颜色改变时用线段树合并一下即可。时间复杂度O( Nlog2N ).

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=4e5+5;
struct code{
    int sum,l;
}d[maxn],b[maxn],c1[maxn];
struct code1{
    int bz,mx;
}f[maxn*4],g[maxn*4];
int first[maxn],last[maxn],next[maxn],value[maxn],c[maxn],size[maxn],mx[maxn],bz[maxn],fa[maxn];
int n,m,i,j,t,k,l,x,y,z,r,num,ans,p,num1,num2,mx1;
void lian(int x,int y,int z){
    last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;
}
bool cmp(code x,code y){
    return x.sum<y.sum;
}
void find(int l,int r,int v,int x,int y,int bz){
    int mid=(l+r)/2;
    if (f[v].bz!=bz) return;
    if (l>=x && r<=y){
        k=max(k,f[v].mx);return;
    }
    if (l<=y && mid>=x) find(l,mid,v*2,x,y,bz);
    if (mid<y && r>=x) find(mid+1,r,v*2+1,x,y,bz);
}
void find1(int l,int r,int v,int x,int y,int bz){
    int mid=(l+r)/2;
    if (g[v].bz!=bz) return;
    if (l>=x && r<=y){
        k=max(k,g[v].mx);return;
    }
    if (l<=y && mid>=x) find1(l,mid,v*2,x,y,bz);
    if (mid<y && r>=x) find1(mid+1,r,v*2+1,x,y,bz);
}
void make(int v,int bz){
    f[v].mx=-2e9;
    if (f[v*2].bz==bz) f[v].mx=f[v*2].mx;
    if (f[v*2+1].bz==bz) f[v].mx=max(f[v].mx,f[v*2+1].mx);
}
void make1(int v,int bz){
    g[v].mx=-2e9;
    if (g[v*2].bz==bz) g[v].mx=g[v*2].mx;
    if (g[v*2+1].bz==bz) g[v].mx=max(g[v].mx,g[v*2+1].mx);
}
void insert1(int l,int r,int v,int y,int x,int bz){
    int mid=(l+r)/2;
    if (g[v].bz!=bz) g[v].mx=-2e9,g[v].bz=bz;
    if (l==r){
        g[v].mx=max(y,g[v].mx);return;
    }
    if (mid>=x) insert1(l,mid,v*2,y,x,bz);
    else insert1(mid+1,r,v*2+1,y,x,bz);
    make1(v,bz);
}
void insert(int l,int r,int v,int y,int x,int bz){
    int mid=(l+r)/2;
    if (f[v].bz!=bz) f[v].mx=-2e9,f[v].bz=bz;
    if (l==r){
        f[v].mx=max(y,f[v].mx);return;
    }
    if (mid>=x) insert(l,mid,v*2,y,x,bz);
    else insert(mid+1,r,v*2+1,y,x,bz);
    make(v,bz);
}
void dg1(int x,int y){
    int t;size[x]=1,mx[x]=0;
    for (t=first[x];t;t=next[t]){
        if (bz[last[t]] || last[t]==y) continue;
        dg1(last[t],x);size[x]+=size[last[t]];mx[x]=max(mx[x],size[last[t]]);
    }
}
void dg2(int x,int y){
    int t,k=num;
    if (d[k].l==r) return;
    for (t=first[x];t;t=next[t]){
        if (bz[last[t]] || last[t]==y) continue;
        d[++num].l=d[k].l+1;d[num].sum=d[k].sum;
        if (value[t]!=fa[x]) d[num].sum+=c[value[t]];fa[last[t]]=value[t];
        dg2(last[t],x); 
    }
}
int find(int x,int y){
    int t,k;
    if (max(mx[x],p-size[x])*2<=p || p==1) return x;
    for (t=first[x];t;t=next[t]){
        if (bz[last[t]] || last[t]==y) continue;
        k=find(last[t],x);if (k) return k;
    }
    return 0;
}
void dg(int x){
    dg1(x,0);p=size[x];
    if (mx1*p<=ans) return;
    x=find(x,0);
    bz[x]=1;int t;num=0;
    for (t=first[x];t;t=next[t]){
        if (bz[last[t]]) continue;
        b[++num].sum=value[t];b[num].l=last[t];
    }
    sort(b+1,b+num+1,cmp);num2=num;
    for (i=1;i<=num2;i++){
        d[num=1].sum=c[b[i].sum];d[1].l=1;fa[b[i].l]=b[i].sum;
        dg2(b[i].l,x);
        for (j=1;j<=num;j++){
            k=-2e9;
            find(1,n,1,max(1,l-d[j].l),r-d[j].l,x);
            find1(1,n,1,max(1,l-d[j].l),r-d[j].l,b[i].sum);
            if (k!=-2e9)ans=max(ans,k+d[j].sum);
            if (d[j].l>=l) ans=max(ans,d[j].sum);
            c1[++num1]=d[j];
        }
        for (j=1;j<=num;j++)
            insert1(1,n,1,d[j].sum-c[b[i].sum],d[j].l,b[i].sum);
        if (b[i+1].sum!=b[i].sum){
            while (num1) insert(1,n,1,c1[num1].sum,c1[num1].l,x),num1--;
        }
    }
    for (t=first[x];t;t=next[t])
        if (!bz[last[t]]) dg(last[t]);
}
int main(){
    freopen("journey.in","r",stdin);freopen("journey.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&l,&r);mx1=-2e9;
    for (i=1;i<=m;i++)
        scanf("%d",&c[i]),mx1=max(mx1,c[i]);
    for (i=1;i<n;i++)
        scanf("%d%d%d",&x,&y,&z),lian(x,y,z),lian(y,x,z);
    p=n;ans=-2e9;
    dg(n/2);printf("%d\n",ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值