【GDOI2017模拟】树的难题

8 篇文章 0 订阅
3 篇文章 0 订阅

Description

这里写图片描述

Solution

比较明显的树分治,把树分治之后直接对其子树进行处理,因为这道题目并不需要去掉子树的重复贡献,比起其他的一些题就要更好处理了。
对于一个以重心为根的子树,关键在于如何把每一棵子树都合并起来,可以发现要分成两种情况来计算贡献:当前根到子树的颜色与要合并子树的颜色相同或不同,因为相同就要减去重复的边的贡献,为了方便处理,我们要把子树的颜色排序,然后用树状数组维护。

Code

#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define rep(i,x) for(i=la[x];i;i=ne[i])
typedef long long ll;
const int N=2e5+5,INF=-0x7fffffff/3;
struct arr{
    int x,se;
}son[N];
int node[N],da[N*2],ne[N*2],la[N],v[N],col[N*2];
int t[3][N],T[3][N],d[3][N],de[N],lc[N],fa[N],D[N];
ll g[N],ans,mx;
int n,m,L,R,i,j,x,y,z,l,r,sum,num,tot,hea;
bool p[N],P[3][N],bz[N];
bool cmp(arr x,arr y){return x.se<y.se;}
void ins(int x,int y,int z){
    da[++sum]=y,ne[sum]=la[x],la[x]=sum,col[sum]=z;
    da[++sum]=x,ne[sum]=la[y],la[y]=sum,col[sum]=z;
}
void gnode(int x){
    int i,l=0,r=1;D[1]=x;fa[x]=0;
    while(l<r){
        x=D[++l];
        rep(i,x) if(da[i]!=fa[x]&&!p[da[i]]) D[++r]=da[i],fa[da[i]]=x;
    }l++;
    while(l>1){
        x=D[--l];node[x]=1;
        rep(i,x) if(da[i]!=fa[x]&&!p[da[i]]) node[x]+=node[da[i]];
    }
}
void gheav(int x,int size){
    int i,l=0,r=1;bz[x]=1;D[1]=x;fa[x]=0;
    while(l<r){
        x=D[++l];
        rep(i,x) if(da[i]!=fa[x]&&!p[da[i]]){
            if(node[da[i]]>size/2) bz[x]=0;
            D[++r]=da[i];bz[da[i]]=1;fa[da[i]]=x;
        }
    }l++;
    while(l>1){
        x=D[--l];if(bz[x]&&size-node[x]<=size/2) hea=x;
    }
}
void sao(int x){
    int i,l,r;
    l=0;r=1;D[1]=x;
    while(l<r){
        x=D[++l];
        if(de[x]>R) return;
        if(de[x]>=L) ans=(ans>g[x])?ans:g[x];
        d[2][++d[2][0]]=de[x];P[2][de[x]]=1;
        t[2][de[x]]=T[2][de[x]]=(t[2][de[x]]>g[x])?t[2][de[x]]:g[x];
        rep(i,x) if(da[i]!=fa[x]&&!p[da[i]]){
            de[da[i]]=de[x]+1;lc[da[i]]=col[i];g[da[i]]=g[x];
            if(lc[x]!=col[i]) g[da[i]]+=v[col[i]];
            fa[da[i]]=x;D[++r]=da[i];
        }
    }
}
void ask(int x,int l,int r){
    tot=INF;
    while(l<=r){
        if (r-(r&-r)>=l) tot=(t[x][r]>tot)?t[x][r]:tot,r-=r&-r;
        else tot=(T[x][r]>tot)?T[x][r]:tot,r--;
    }
}
void add(int x,int y,int z,int a){
    T[x][y]=(T[x][y]>a)?T[x][y]:a;
    for(;y<=n;y+=(y&-y)){
        if(!P[x][y]) P[x][y]=1,d[x][++d[x][0]]=y;
        t[x][y]=(t[x][y]>z)?t[x][y]:z;
    }
}
void fill(int x){
    int i;fo(i,1,d[x][0]) P[x][d[x][i]]=0,t[x][d[x][i]]=T[x][d[x][i]]=INF; d[x][0]=0;
}
void merge(int x,int y){
    int i;
    fo(i,1,d[y][0]){
        add(x,d[y][i],t[y][d[y][i]],T[y][d[y][i]]);
        P[y][d[y][i]]=0,t[y][d[y][i]]=T[y][d[y][i]]=INF;
    }d[y][0]=0;
}
void divi(int x){
    int i,j;bool bz;
    gnode(x);hea=0;gheav(x,node[x]);x=hea;
    if(ans>mx*node[x]) return;
    p[x]=true;num=0;
    rep(i,x) if(!p[da[i]]) son[++num].x=da[i],son[num].se=col[i];
    sort(son+1,son+num+1,cmp);
    fo(i,1,num){
        bz=(i-1)?1:0;
        if(i!=1&&son[i-1].se!=son[i].se) merge(0,1),bz=0;
        de[son[i].x]=1,lc[son[i].x]=son[i].se,g[son[i].x]=v[son[i].se],fa[son[i].x]=x;
        sao(son[i].x);
        fo(j,1,d[2][0]){
            l=(L-d[2][j]>1)?L-d[2][j]:1;r=R-d[2][j];
            if(bz){
                ask(1,l,r); tot+=-v[son[i].se]+t[2][d[2][j]];
                ans=(ans>tot)?ans:tot;
            }
            ask(0,l,r);tot+=t[2][d[2][j]];
            ans=(ans>tot)?ans:tot;
        }
        merge(1,2);
    }
    fill(0);if(son[num-1].se==son[num].se)fill(1);
    rep(i,x) if(!p[da[i]]) divi(da[i]);
}
int main(){
    freopen("journey.in","r",stdin);
    freopen("journey.out","w",stdout);

    scanf("%d%d%d%d",&n,&m,&L,&R);
    fo(i,0,2) fo(j,0,n) t[i][j]=T[i][j]=INF;
    ans=mx=INF;
    fo(i,1,m) scanf("%d",&v[i]),mx=(mx>v[i])?mx:v[i];
    fo(i,1,n-1){
        scanf("%d%d%d",&x,&y,&z);
        ins(x,y,z);
    }
    divi(1);
    printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值