[bzoj4860] [BeiJing2017]樹的難題
阿新 • • 發佈:2018-12-28
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; }