[bzoj4860] [BeiJing2017]树的难题

Description

给你一棵 n 个点的无根树。树上的每条边具有颜色。 一共有 m 种颜色,编号为 1 到 m。第 i 种颜色的权值为

ci。对于一条树上的简单路径,路径上经过的所有边按顺序组成一个颜色序列,序列可以划分成若干个相同颜色段

。 定义路径权值为颜色序列上每个同颜色段的颜色权值之和。请你计算,经过边数在 l 到 r 之间的所有简单路

径中, 路径权值的最大值。

Input

第一行, 四个整数 n, m, l, r。

第二行, n 个整数 c1, c2, ……, cm,由空格隔开。依次表示每个颜色的权值。

接下来 n-1 行,每行三个整数 u, v, c,表示点 u 和点 v 之间有一条颜色为 c 的边。

Output

输出一行, 一个整数, 表示答案。

Sample Input

5 3 1 4
-1 -5 -2
1 2 1
1 3 1
2 4 2
2 5 3

Sample Output

-1

Solution

统计链上信息,上点分治。

对于当前的分治中心,把边按颜色\(sort\)一遍,然后开两个线段树,对于颜色相同的边用一颗线段树统计,不同的也开一颗,做完一种颜色就把两颗线段树合并一下,然后清空就好了。

复杂度\(O(n\log^2n)\),然后凭借信仰过掉此题(跑的还蛮快的)

不过正解好像是单调队列,我太弱了不会QAQ

#include<bits/stdc++.h>
using namespace std;

inline int max(int x,int y) {return x>y?x:y;}
#define il inline

il void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
il void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
il void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 6e5+10;
const int inf = 1e9;

int n,m,l,r,tot,st[maxn],ed[maxn],ls[maxn<<2],rs[maxn<<2],tr[maxn<<2],cnt;

struct edge{
	int fr,to,w;
	int operator < (const edge &rhs) const {
		return fr<rhs.fr||(fr==rhs.fr&&w<rhs.w);
	}
}e[maxn<<1];

il void add(int u,int v,int w) {e[++tot]=(edge){u,v,w};}
il void ins(int u,int v,int w) {add(u,v,w),add(v,u,w);}

#define mid ((l+r)>>1)

namespace Segment_Tree {
	void modify(int &p,int l,int r,int x,int v) {
		if(!p) p=++cnt,ls[p]=rs[p]=0,tr[p]=-inf;
		else tr[p]=max(tr[p],v);
		if(l==r) return ;
		if(x<=mid) modify(ls[p],l,mid,x,v);
		else modify(rs[p],mid+1,r,x,v);
	}
	int query(int p,int l,int r,int x,int y) {
		if(!p) return -inf;
		if(x<=l&&r<=y) return tr[p];
		int ans=-inf;
		if(x<=mid) ans=max(ans,query(ls[p],l,mid,x,y));
		if(y>mid) ans=max(ans,query(rs[p],mid+1,r,x,y));
		return ans;
	}
	int merge(int x,int y) {
		if(!x||!y) return x+y;
		ls[x]=merge(ls[x],ls[y]);
		rs[x]=merge(rs[x],rs[y]);
		tr[x]=max(tr[x],tr[y]);
		return x;
	}
}

#undef mid 

using namespace Segment_Tree;

namespace Tree {
	int rt,f[maxn],sz[maxn],size,vis[maxn],data_cnt,c[maxn],ans,ss[maxn];

	void get_rt(int x,int fa) {
		sz[x]=1,f[x]=1;
		for(int i=st[x];i<=ed[x];i++)
			if(e[i].to!=fa&&(!vis[e[i].to])) 
				get_rt(e[i].to,x),sz[x]+=sz[e[i].to],f[x]=max(f[x],sz[e[i].to]);
		f[x]=max(f[x],size-sz[x]);
		if(f[rt]>f[x]) rt=x;
	}

	struct data {int dep,dis;}d[maxn];

	void get_data(int x,int fa,int dep,int col,int sum) {
		if(dep>r) return ;
		for(int i=st[x];i<=ed[x];i++) {
			if(e[i].to==fa||vis[e[i].to]) continue;
			d[++data_cnt]=(data){dep+1,sum+((col==e[i].w)?0:c[e[i].w])};
			get_data(e[i].to,x,dep+1,e[i].w,sum+((col==e[i].w)?0:c[e[i].w]));
		}
	}

	void solve(int x) {
		vis[x]=1;int r1=0,r2=cnt=0;
		for(int i=st[x];i<=ed[x];++i) {
			if(e[i].w!=e[i-1].w&&i!=st[x]) r1=merge(r1,r2),r2=0;
			if(vis[e[i].to]) continue;
			data_cnt=0;
			d[++data_cnt]=(data){1,c[e[i].w]};
			get_data(e[i].to,x,1,e[i].w,c[e[i].w]);
			ss[i]=data_cnt;
			for(int j=1;j<=data_cnt;++j) {
				if(d[j].dep>r) continue;
				if(d[j].dep<r) {
					ans=max(ans,d[j].dis+query(r2,1,n,max(1,l-d[j].dep),r-d[j].dep)-c[e[i].w]);
					ans=max(ans,d[j].dis+query(r1,1,n,max(1,l-d[j].dep),r-d[j].dep));
				}
				if(d[j].dep>=l) ans=max(ans,d[j].dis);
			}
			for(int j=1;j<=data_cnt;++j)
				if(d[j].dep<r) modify(r2,1,n,d[j].dep,d[j].dis);
		}
		for(int i=st[x];i<=ed[x];++i) {
			if(vis[e[i].to]) continue;
			rt=0,size=ss[i],get_rt(e[i].to,x);
			solve(rt);
		}
	}
}

using namespace Tree;

int main() {
	read(n),read(m),read(l),read(r);ans=-inf;
	for(int i=1;i<=m;i++) read(c[i]);
	for(int i=1,x,y,z;i<n;i++) read(x),read(y),read(z),ins(x,y,z);
	sort(e+1,e+n*2-1);int p=1;
	for(int i=1;i<=n;i++) {
		st[i]=p;while(e[p].fr==i&&p<=n*2-2) p++;ed[i]=p-1;
	}
	rt=0,f[0]=maxn+1,size=n,get_rt(1,0),solve(rt);write(ans);
	return 0;
}
posted @ 2018-12-28 10:49  Hyscere  阅读(240)  评论(0编辑  收藏  举报