【GDOI2017第三轮模拟day2】树的难题(点剖,树状数组)

18 篇文章 0 订阅
7 篇文章 0 订阅

Description

这里写图片描述

Solution

一看就知道是点剖,但是还要套上一个数据结构,感觉很麻烦,比赛的时候直接上暴力。
点剖的时候在分治中心的时候,因为发现颜色是一个很麻烦的东西,所以考虑对直系儿子的颜色进行排序,然后对同一个颜色的开一个树状数组,然后对整体开一个树状数组,用树状数组log方的时间求最大值(如果r-lowbit < l那么r–)
这样做是 nlog3n 的,但是因为树状数组的常数比较小,所以比很多log方线段树的人跑的都快。
但是还是要打一个小优化,如果mx(颜色最大值)*size>ans那么就退出

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
typedef long long ll;
const int maxn=2e5+7,inf=0x7fffffff/2;
int i,j,k,l,n,m,r,x,y,z,tot,tou,tot1,qian;
int c[maxn],first[maxn*2],last[maxn*2],next[maxn*2],chang[maxn*2],num,size[maxn],d[2][maxn];
bool bz[maxn],az[2][maxn];
ll t[2][maxn],g[2][maxn],o,w,da,dian,ans;
struct node{
    int se,len;ll sum;
}a[maxn];
struct nod{
    int se,a;
}b[maxn];
bool cmp(nod x,nod y){return x.se<y.se;}
void ad(int x,int y,int z){
    last[++num]=y,next[num]=first[x],first[x]=num,chang[num]=z;
}
void clear(int o){
    int i,p;fo(i,1,d[o][0])p=d[o][i],az[o][p]=0,t[o][p]=g[o][p]=-inf;
    d[o][0]=0;
}
void add(int o,int x,ll y){
    g[o][x]=(g[o][x]<y)?y:g[o][x];
    for(;x<=maxn-7;x+=(x&-x)){
        if(!az[o][x])d[o][++d[o][0]]=x,az[o][x]=1;
        t[o][x]=(t[o][x]<y)?y:t[o][x];
    }
}
int find(int o,int l,int r){
    ll z=-inf;
    while(l<=r){
        if(r-(r&-r)+1>=l)z=(z<t[o][r])?t[o][r]:z,r-=(r&-r);
        else z=(z<g[o][r])?g[o][r]:z,r--;
    }
    return z;
}
void dfs1(int x,int y){
    int i;size[x]=1;
    rep(i,x)if(!bz[last[i]]&&last[i]!=y)dfs1(last[i],x),size[x]+=size[last[i]];
}
void dfs2(int x,int y){
    int i;bool az=1;
    rep(i,x)if(!bz[last[i]]&&last[i]!=y){
        if(size[last[i]]>dian/2)az=0;
        dfs2(last[i],x);
    }
    if(az&&dian-size[x]<=dian/2)z=x;
}
void dfs3(int x,int y,int z,int u,ll sum){
    int i;
    if(z>r)return;
    a[++tot]=(node){tou,z,sum};
    if(z>=l)ans=(ans<sum)?sum:ans;
    rep(i,x){
        if(last[i]!=y&&!bz[last[i]]){
            if(u==chang[i])dfs3(last[i],x,z+1,u,sum);
            else dfs3(last[i],x,z+1,chang[i],sum+c[chang[i]]);
        }
    }
}
void fen(int x){
    int i,j;
    dfs1(x,0);dian=size[x];dfs2(x,0);x=z;bz[x]=1;
    if(ans>da*dian)return;
    tot1=tot=0;
    rep(i,x)if(!bz[last[i]])b[++tot1].se=chang[i],b[tot1].a=last[i];
    sort(b+1,b+1+tot1,cmp);
    clear(0);
    b[++tot1].se=b[tot1-1].se+1;b[0].se=b[1].se;
    fo(i,1,tot1){
        if(b[i].se!=b[i-1].se){
            fo(j,1,tot){
                w=(1<l-a[j].len)?l-a[j].len:1;
                o=find(0,w,r-a[j].len)+a[j].sum;
                ans=(ans<o)?o:ans;
            }
            if(i==tot1)continue;
            fo(j,1,tot)add(0,a[j].len,a[j].sum);tot=0;
            clear(1);
        }
        tou=b[i].se;qian=tot;
        dfs3(b[i].a,0,1,b[i].se,c[b[i].se]);
        if(b[i].se==b[i-1].se){
            fo(j,qian+1,tot){
                w=(1<l-a[j].len)?l-a[j].len:1;
                o=find(1,w,r-a[j].len)+a[j].sum-c[a[j].se];
                ans=(ans<o)?o:ans;
            }
            fo(j,qian+1,tot)add(1,a[j].len,a[j].sum);
        }

    }
    clear(1);
    rep(i,x)if(!bz[last[i]])fen(last[i]);
}
int main(){
    freopen("journey.in","r",stdin);
    freopen("journey.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&l,&r);ans=da=-inf;
    fo(i,1,m)scanf("%d",&c[i]),da=(da<c[i])?c[i]:da;
    fo(i,1,n-1){
        scanf("%d%d%d",&x,&y,&z);
        ad(x,y,z);ad(y,x,z);
        t[0][i]=t[1][i]=g[0][i]=g[1][i]=-inf;
    }
    fen(n/2);
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值