这个思路还是非常巧妙的.
困难在于我们需要同时维护以 $x$ 为分治中心,延伸出颜色相同/不同的最大值.
不同的话直接将权和相加,相同的话还需要减掉重复部分,这就比较难办.
但是我们发现,当以 $x$ 为分治中心时,$x$ 每一个儿子为根的子树的延伸颜色都是相同的.
所以我们可以将每一个点的所有儿子按照延伸颜色排序,然后维护两颗线段树:相同与不同.
当我们在点分治时处理到下一个儿子,而下一个儿子与当前儿子颜色不同时,用线段树合并的方式将相同线段树合并到不同即可.
code:
#include
#include
#include
#include
#include
#define N 200006
#define ll long long
#define inf 1000000009
using namespace std;
void setIO(string s)
{
freopen((s+".in").c_str(),"r",stdin);
// freopen((s+".out").c_str(),"w",stdout);
}
int answer=-inf,mp=0;
namespace seg
{
int tot;
struct node { int ls,rs,maxx; }s[N*50];
void clr()
{
for(int i=1;i<=tot;++i) s[i].ls=s[i].rs=0,s[i].maxx=-inf;
tot=0;
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
s[x].maxx=max(s[x].maxx,s[y].maxx);
s[x].ls=merge(s[x].ls,s[y].ls);
s[x].rs=merge(s[x].rs,s[y].rs);
return x;
}
void update(int &x,int l,int r,int p,int v)
{
if(!x) x=++tot,s[x].maxx=-inf;
s[x].maxx=max(s[x].maxx,v);
if(l==r) return;
int mid=(l+r)>>1;
if(p<=mid) update(s[x].ls,l,mid,p,v);
else update(s[x].rs,mid+1,r,p,v);
}
int query(int x,int l,int r,int L,int R)
{
if(!x||L>R) return -inf;
if(l>=L&&r<=R) return s[x].maxx;
int mid=(l+r)>>1,re=-inf;
if(L<=mid) re=max(re,query(s[x].ls,l,mid,L,R));
if(R>mid) re=max(re,query(s[x].rs,mid+1,r,L,R));
return re;
}
};
int n,m,_min,_max,root,sn,tot;
int val[N],size[N],mx[N],vis[N];
struct Edge
{
int to,w;
Edge(int to=0,int w=0):to(to),w(w){}
};
struct Dis
{
int d,v;
Dis(int d=0,int v=0):d(d),v(v){}
}A[N];
vectorF[N];
vectorG[N];
bool cmp(Edge a,Edge b) { return a.w_max) continue;
if(A[j].d>=_min&&A[j].d<=_max)
{
answer=max(answer,A[j].v),mp=max(mp,A[j].v);
}
answer=max(answer,A[j].v+seg::query(rt_diff,1,n,max(1,_min-A[j].d),_max-A[j].d));
answer=max(answer,A[j].v+seg::query(rt_same,1,n,max(1,_min-A[j].d),_max-A[j].d)-val[F[u][i].w]);
}
for(int j=1;j<=tot;++j) seg::update(rt_same,1,n,A[j].d,A[j].v);
if(i