给定一棵 n 个点的无根树,树上每一条边都有颜色。一共 m 种颜色,编号从 1 到 m 。第 i 种颜色权值为 ci 。
对于树上的一条简单路径,路径上经过的所有边按照顺序组成一个颜色序列,序列可以划分成若干个相同颜色段。定义路径权值为颜色序列上每一个同颜色段的颜色权值之和。
你要计算出边数在 [l,r] 之内的所有简单路径中,路径权值的最大值。
1≤m≤n≤2×105,1≤l≤r≤n,|ci|≤104
点分治,对于一个分治重心,把子树按照到重心边的颜色排序,然后同种颜色同种颜色转移,用线段树记录长度为某个值的最大答案,开两棵来进行不同颜色和同颜色的转移。在切换颜色时使用线段树合并将第二棵并到第一棵上可以卡常。
时间复杂度 O(nlog2n) 。
然而,如果我们同颜色按深度排排序,不同颜色也排排序,姿势好一点的话用单调队列可以做到 O(nlogn) 。
蒟蒻在模拟赛时只想到了 log2 做法。
#include
#include
#include
#include
#include
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int INF=INT_MAX;
const int N=200050;
const int M=200050;
const int E=N<<1;
const int LGN=18;
const int S=N*LGN*2;
struct segment_tree
{
int v[S],son[S][2];
int tot;
void init(){tot=0,v[0]=-INF;}
int newnode(){return v[++tot]=-INF,son[tot][0]=son[tot][1]=0,tot;}
void insert(int &rt,int x,int y,int l,int r)
{
if (!rt) rt=newnode();
v[rt]=max(v[rt],y);
if (l==r) return;
int mid=l+r>>1;
if (x<=mid) insert(son[rt][0],x,y,l,mid);
else insert(son[rt][1],x,y,mid+1,r);
}
int query(int rt,int st,int en,int l,int r)
{
if (!rt) return -INF;
if (st==l&&en==r) return v[rt];
int mid=l+r>>1;
if (en<=mid) return query(son[rt][0],st,en,l,mid);
else if (mid+1<=st) return query(son[rt][1],st,en,mid+1,r);
else return max(query(son[rt][0],st,mid,l,mid),query(son[rt][1],mid+1,en,mid+1,r));
}
int merge(int rt1,int rt2)
{
if (!(rt1&&rt2)) return rt1^rt2;
v[rt1]=max(v[rt1],v[rt2]);
son[rt1][0]=merge(son[rt1][0],son[rt2][0]),son[rt1][1]=merge(son[rt1][1],son[rt2][1]);
return rt1;
}
}t;
struct data
{
int x,c;
bool operator<(data const d)const{return cint last[N],fa[N],size[N],que[N];
int tov[E],nxt[E],col[E];
bool vis[N];
int val[M];
int n,m,tot,head,tail,ans,L,R,root1,root2,dif;
void insert(int x,int y,int z){tov[++tot]=y,nxt[tot]=last[x],col[tot]=z,last[x]=tot;}
int core(int og)
{
int rets=n,ret,x,y,i,tmp;
for (head=0,fa[que[tail=1]=og]=0;headfor (size[x=que[++head]]=1,i=last[x];i;i=nxt[i])
if ((y=tov[i])!=fa[x]&&!vis[y]) fa[que[++tail]=y]=x;
for (head=tail;head>1;--head) size[fa[que[head]]]+=size[que[head]];
for (head=1;head<=tail;++head)
{
for (tmp=size[og]-size[x=que[head]],i=last[x];i;i=nxt[i])
if ((y=tov[i])!=fa[x]&&!vis[y]) tmp=max(tmp,size[y]);
if (tmpreturn ret;
}
void calc(int x,int len,int lst,int cur)
{
if (len>R) return;
if (L<=len) ans=max(ans,cur);
if (lenint ret=t.query(root1,max(L-len,1),R-len,1,n);
if (ret!=-INF) ans=max(ans,ret+cur);
ret=t.query(root2,max(L-len,1),R-len,1,n);
if (ret!=-INF) ans=max(ans,ret+cur-dif);
}
for (int i=last[x],y;i;i=nxt[i])
if ((y=tov[i])!=fa[x]&&!vis[y])
fa[y]=x,calc(y,len+1,col[i],cur+(lst!=col[i])*val[col[i]]);
}
void change(int x,int len,int lst,int cur)
{
if (len>R) return;
t.insert(root2,len,cur,1,n);
for (int i=last[x],y;i;i=nxt[i])
if ((y=tov[i])!=fa[x]&&!vis[y])
change(y,len+1,col[i],cur+(lst!=col[i])*val[col[i]]);
}
void solve(int x)
{
int c=core(x),cnt=0;
for (int i=last[c],y;i;i=nxt[i])
if (!vis[y=tov[i]]) son[++cnt].x=y,son[cnt].c=col[i];
sort(son+1,son+1+cnt),root1=root2=0,t.init();
for (int j=1;j<=cnt;++j)
{
if (son[j].c!=son[j-1].c) root1=t.merge(root1,root2),root2=0,dif=val[son[j].c];
fa[son[j].x]=c,calc(son[j].x,1,son[j].c,val[son[j].c]),change(son[j].x,1,son[j].c,val[son[j].c]);
}
vis[c]=1;
for (int i=last[c],y;i;i=nxt[i])
if (!vis[y=tov[i]]) solve(y);
}
int main()
{
freopen("journey.in","r",stdin),freopen("journey.out","w",stdout);
n=read(),m=read(),L=read(),R=read();
for (int i=1;i<=m;++i) val[i]=read();
for (int i=1,x,y,z;i1),printf("%d\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}