点分是个好东西。
现在在点分的过程中,我们找到了一个点作为根,然后它有若干子树。显然子树连着根的那条边的颜色影响答案统计,异色子树(即子树根节点与根节点之间的边异色)和同色子树需要分开处理。
把所有子树按照该颜色中最深子树的深度为第一关键字,该子树深度为第二关键字,从小到大排序。然后按照这种顺序进行处理。
维护两个值v0(x)和v1(x),分别表示同色和异色子树中的路径里,可以与一条长度为x的路径组合成新路径的路中,价值最大的路的价值。(有点绕口QAQ)
这个可以用单调队列维护。
那么为什么要排序呢,是因为要确保复杂度。
具体实现还是看代码吧…….
#include
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.colbool cmp2(node x,node y) {
if(x.mxd!=y.mxd) return x.mxdif(x.col!=y.col) return x.colreturn x.dvoid 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(blint 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;i1,0),work(rt);
printf("%d\n",ans);
return 0;
}