洛谷P3714/loj2179/bzoj4860 树的难题 点分治+单调队列

题目分析

点分是个好东西。

现在在点分的过程中,我们找到了一个点作为根,然后它有若干子树。显然子树连着根的那条边的颜色影响答案统计,异色子树(即子树根节点与根节点之间的边异色)和同色子树需要分开处理。

把所有子树按照该颜色中最深子树的深度为第一关键字,该子树深度为第二关键字,从小到大排序。然后按照这种顺序进行处理。

维护两个值v0(x)和v1(x),分别表示同色和异色子树中的路径里,可以与一条长度为x的路径组合成新路径的路中,价值最大的路的价值。(有点绕口QAQ)
这个可以用单调队列维护。
那么为什么要排序呢,是因为要确保复杂度。

具体实现还是看代码吧…….

代码

#include<bits/stdc++.h>
using namespace std;
int read() {
    int q=0,w=1;char ch=' ';
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return w*q;
}
#define RI register int
const int N=200005,inf=0x3f3f3f3f;
int n,m,l,r,tot,mx,SZ,rt,du,ans,orzd;
int c[N],h[N],ne[N<<1],to[N<<1],w[N<<1],vis[N],sz[N];
int b[N],v[N];
int v0[N],q0[N],bj0[N],mx0[N],h0,t0;//不同色子树
int v1[N],q1[N],bj1[N],mx1[N],h1,t1;//同色子树
struct node{int col,d,id,mxd;}a[N];
bool cmp1(node x,node y) {return x.col==y.col?x.d>y.d:x.col<y.col;}
bool cmp2(node x,node y) {
    if(x.mxd!=y.mxd) return x.mxd<y.mxd;
    if(x.col!=y.col) return x.col<y.col;
    return x.d<y.d;
}

void add(RI x,RI y,RI z) {to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
void getrt(RI x,RI las) {
    sz[x]=1; RI bl=0;
    for(RI i=h[x];i;i=ne[i])
        if(to[i]!=las&&!vis[to[i]])
            getrt(to[i],x),sz[x]+=sz[to[i]],bl=max(bl,sz[to[i]]);
    bl=max(bl,SZ-sz[x]);
    if(bl<mx) mx=bl,rt=x;
}
int getdep(RI x,RI las,RI nowd) {//获得子树深度
    RI re=nowd;
    for(RI i=h[x];i;i=ne[i])
        if(to[i]!=las&&!vis[to[i]]) re=max(re,getdep(to[i],x,nowd+1));
    return re;
}
void dfs(RI x,RI las,RI nowd,RI nowv,RI lasc) {
    if(nowd<=orzd) b[nowd]=max(b[nowd],nowv);//b[i]:这棵子树中深度为i的路径的最大价值
    else b[nowd]=nowv,orzd=nowd;
    ans=max(ans,nowv+max(v0[nowd],v1[nowd]));//统计答案
    for(RI i=h[x];i;i=ne[i]) {
        if(to[i]==las||vis[to[i]]) continue;
        if(w[i]!=lasc) dfs(to[i],x,nowd+1,nowv+c[w[i]],w[i]);
        else dfs(to[i],x,nowd+1,nowv,w[i]);
    }
}
void work(RI x) {
    vis[x]=1,du=0;
    for(RI i=h[x];i;i=ne[i])
        if(!vis[to[i]]) a[++du]=(node){w[i],getdep(to[i],x,1),to[i]};
    sort(a+1,a+1+du,cmp1);
    for(RI i=1;i<=du;++i)//获得某一种颜色的最大深度
        a[i].mxd=(a[i].col==a[i-1].col?a[i-1].mxd:a[i].d);
    sort(a+1,a+1+du,cmp2);
    for(RI i=1;i<=a[du].mxd;++i) mx0[i]=mx1[i]=v0[i]=v1[i]=-inf;
    for(RI i=l;i<=a[du].mxd&&i<=r;++i) v1[i]=0;//异色连通块有一条长度为0的路

    for(RI i=1;i<=du;++i) {
        orzd=0,dfs(a[i].id,x,1,c[a[i].col],a[i].col);
        if(i==du) break;
        if(a[i].col==a[i+1].col) {
            for(RI j=1;j<=orzd;++j) mx0[j]=max(mx0[j],b[j]);//更新同色情况下深度为j的路径最大价值
            h0=1,t0=0,mx0[0]=c[a[i].col];
            for(RI j=min(r-1,orzd);j>=l-1;--j) {//从深度大到小加入单调队列
                while(h0<=t0&&q0[t0]<=mx0[j]) --t0;
                q0[++t0]=mx0[j],bj0[t0]=j;//bj:深度
            }
            for(RI j=1;j<=a[i+1].d;++j) {
                while(h0<=t0&&bj0[h0]+j>r) ++h0;
                if(h0<=t0) v0[j]=q0[h0]-c[a[i].col];//a[i].col会被两棵同色子树计算两次
                else v0[j]=-inf;
                if(l-j-1>=0&&l-j-1<=orzd) {
                    while(h0<=t0&&q0[t0]<=mx0[l-j-1]) --t0;
                    q0[++t0]=mx0[l-j-1],bj0[t0]=l-j-1;
                }
            }
        }
        else {
            for(RI j=1;j<=orzd;++j) mx1[j]=max(mx1[j],max(b[j],mx0[j]));//更新异色情况下深度为j的路径最大价值
            h1=1,t1=0,mx1[0]=0;
            for(RI j=min(r-1,orzd);j>=l-1;--j) {
                while(h1<=t1&&q1[t1]<=mx1[j]) --t1;
                q1[++t1]=mx1[j],bj1[t1]=j;
            }
            for(RI j=1;j<=a[i+1].mxd;++j) {//注意此处是到mxd
                while(h1<=t1&&bj1[h1]+j>r) ++h1;
                if(h1<=t1) v1[j]=q1[h1];
                else v1[j]=-inf;
                if(l-j-1>=0&&l-j-1<=orzd) {
                    while(h1<=t1&&q1[t1]<=mx1[l-j-1]) --t1;
                    q1[++t1]=mx1[l-j-1],bj1[t1]=l-j-1;
                }
            }
            for(RI j=1;j<=a[i+1].mxd;++j) mx0[j]=v0[j]=-inf;//清空同色情况的数组
        }
    }
    for(RI i=h[x];i;i=ne[i])
        if(!vis[to[i]]) mx=inf,SZ=sz[to[i]],getrt(to[i],x),work(rt);
}
int main()
{i
    RI x,y,z;
    n=read(),m=read(),l=read(),r=read();
    for(RI i=1;i<=m;++i) c[i]=read();
    for(RI i=1;i<n;++i)
        x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z);
    ans=-inf;
    mx=inf,SZ=n,getrt(1,0),work(rt);
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/79850640
今日推荐