[国家集训队]旅游https://www.luogu.org/problemnew/show/P1505
题目描述
\(Ray\)乐忠于旅游,这次他来到了\(T\)城。\(T\)城是一个水上城市,一共有\(N\)个景点,有些景点之间会用一座桥连接。为了方便游客到达每个景点但又为了节约成本,\(T\)城的任意两个景点之间有且只有一条路径。换句话说,\(T\)城中只有\(N?1\)座桥。
\(Ray\)发现,有些桥上可以看到美丽的景色,让人心情愉悦,但有些桥狭窄泥泞,令人烦躁。于是,他给每座桥定义一个愉悦度\(w\),也就是说,\(Ray\)经过这座桥会增加w 的愉悦度,这或许是正的也可能是负的。有时,\(Ray\)看待同一座桥的心情也会发生改变。
现在,\(Ray\) 想让你帮他计算从\(u\)景点到\(v\)景点能获得的总愉悦度。有时,他还想知道某段路上最美丽的桥所提供的最大愉悦度,或是某段路上最糟糕的一座桥提供的最低愉悦度。
输入格式:
输入的第一行包含一个整数\(N\),表示\(T\)城中的景点个数。景点编号为\(0\)~\(N?1\)。
接下来\(N?1\)行,每行三个整数\(u\)、\(v\)和\(w\),表示有一条\(u\)到\(v\),使\(Ray\)愉悦度增加\(w\)的桥。
桥的编号为\(1\)~\(N?1\).\(|w|<=1000\).
输入的第\(N+1\)行包含一个整数\(M\),表示\(Ray\)的操作数目。
接下来有\(M\)行,每行描述了一个操作,操作有如下五种形式:
\(C\) \(i\) \(w\),表示\(Ray\)对于经过第\(i\)座桥的愉悦度变成了\(w\)。
\(N\) \(u\) \(v\),表示\(Ray\)对于经过景点u到v的路径上的每一座桥的愉悦度都变成原来的相反数。
\(SUM\) \(u\) \(v\),表示询问从景点\(u\)到\(v\)所获得的总愉悦度。
\(MAX\) \(u\) \(v\),表示询问从景点\(u\)到\(v\)的路径上的所有桥中某一座桥所提供的最大愉悦度。
\(MIN\) \(u\) \(v\),表示询问从景点\(u\)到\(v\)的路径上的所有桥中某一座桥所提供的最小愉悦度。
测试数据保证,任意时刻,\(Ray\)对于经过每一座桥的愉悦度的绝对值小于等于\(1000\)。
输出格式:
对于每一个询问(操作\(S\)、\(MAX\) 和\(MIN\)),输出答案。
输入样例:
3
0 1 1
1 2 2
8
SUM 0 2
MAX 0 2
N 0 1
SUM 0 2
MIN 0 2
C 1 3
SUM 0 2
MAX 0 2
输出样例:
3
2
1
-1
5
3
树链剖分
单点修改,区间修改,区间(最大值,最小值,和)查询
关键在于区间修改,题目让取反,可以开一个\(bool\)的\(opp[]\)(相当与\(lazy\))表示是否需要取反,每次opp[]^=1;
注意:
1.题目中没说明\(N\)的范围,我是开到\(5w\)才\(AC\)的.
2.\(Pushdown\)的时候不止修改\(sum\),还有\(Max\)和\(Min\).
3.将边权(父亲-->儿子)赋给儿子的点权
4.由于点权的特殊性,在修改和查询中当节点\(x,y\)跳到同一条链上时,特判\(x==y\)且查询[修改]区间为\((dfn[x]+1~dfn[y])\)
#define RG register
#include<cstdio>
#include<iostream>
using namespace std;
const int N=51000;
inline int read()
{
RG int x=0,w=1;RG char ch=getchar();
while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar();
return x*w;
}
int n,m,cnt,ct;
int last[N],size[N],fa[N],dep[N],son[N],val[N],top[N],dfn[N],rk[N];
int sum[N<<2],Max[N<<2],Min[N<<2];
bool opp[N<<2];
struct edge{int to,next,w;}e[N<<1];
inline void insert(int u,int v,int w)
{
e[++cnt]=(edge){v,last[u],w};last[u]=cnt;
e[++cnt]=(edge){u,last[v],w};last[v]=cnt;
}
void dfs1(int now)
{
size[now]=1;
for(int i=last[now];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa[now])continue;
fa[v]=now;
val[v]=e[i].w;
dep[v]=dep[now]+1;
dfs1(v);
size[now]+=size[v];
if(size[v]>size[son[now]])son[now]=v;
}
}
void dfs2(int now,int Top)
{
top[now]=Top;dfn[now]=++ct;rk[ct]=now;
if(son[now])dfs2(son[now],Top);
for(int i=last[now];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa[now]||v==son[now])continue;
dfs2(v,v);
}
}
inline void Pushup(int root)
{
sum[root]=sum[root<<1]+sum[root<<1|1];
Max[root]=max(Max[root<<1],Max[root<<1|1]);
Min[root]=min(Min[root<<1],Min[root<<1|1]);
}
void Build(int root,int l,int r)
{
if(l==r){sum[root]=Max[root]=Min[root]=val[rk[l]];return;}
int mid=(l+r)>>1;
Build(root<<1,l,mid);
Build(root<<1|1,mid+1,r);
Pushup(root);
}
inline void Pushdown(int root)
{
if(!opp[root])return;
opp[root]=false;
sum[root<<1]=-sum[root<<1];sum[root<<1|1]=-sum[root<<1|1];//sum[]直接取反
swap(Max[root<<1],Min[root<<1]);Max[root<<1]=-Max[root<<1];Min[root<<1]=-Min[root<<1];//max[]和min[]先交换后取反
swap(Max[root<<1|1],Min[root<<1|1]);Max[root<<1|1]=-Max[root<<1|1];Min[root<<1|1]=-Min[root<<1|1];
opp[root<<1]^=1;opp[root<<1|1]^=1;//懒标记取反
}
void Modify_Point(int root,int l,int r,int s,int k)
{
Pushdown(root);
if(l==s&&r==s){Max[root]=Min[root]=sum[root]=k;return;}//注意max,min,sum都要修改
int mid=(l+r)>>1;
if(s<=mid)Modify_Point(root<<1,l,mid,s,k);
else Modify_Point(root<<1|1,mid+1,r,s,k);
Pushup(root);
}
void Modify(int root,int l,int r,int ll,int rr)
{
Pushdown(root);
if(ll<=l&&r<=rr)
{
opp[root]^=1;
sum[root]=-sum[root];
swap(Max[root],Min[root]);
Max[root]=-Max[root];
Min[root]=-Min[root];
return;
}
int mid=(l+r)>>1;
if(ll<=mid)Modify(root<<1,l,mid,ll,rr);
if(mid<rr)Modify(root<<1|1,mid+1,r,ll,rr);
Pushup(root);
}
int Query(int root,int l,int r,int ll,int rr,int op)
{
Pushdown(root);
if(ll<=l&&r<=rr)
{
if(op==1)return sum[root];
if(op==2)return Max[root];
if(op==3)return Min[root];
}
int mid=(l+r)>>1,Ans;
if(op==1)Ans=0;
if(op==2)Ans=-1e9;
if(op==3)Ans=1e9;
if(ll<=mid)
{
if(op==1)Ans+=Query(root<<1,l,mid,ll,rr,op);
if(op==2)Ans=max(Ans,Query(root<<1,l,mid,ll,rr,op));
if(op==3)Ans=min(Ans,Query(root<<1,l,mid,ll,rr,op));
}
if(mid<rr)
{
if(op==1)Ans+=Query(root<<1|1,mid+1,r,ll,rr,op);
if(op==2)Ans=max(Ans,Query(root<<1|1,mid+1,r,ll,rr,op));
if(op==3)Ans=min(Ans,Query(root<<1|1,mid+1,r,ll,rr,op));
}
return Ans;
}
inline void Modify_Opp(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
Modify(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
if(x!=y)Modify(1,1,n,dfn[x]+1,dfn[y]);//dfn[x]对应的点权不在修改范围中
}
inline int Query_Tree(int x,int y,int op)//op=1:查询和;op=2:查询最大值;op=3:查询最小值
{
int Ans;
if(op==1)Ans=0;
if(op==2)Ans=-1e9;//注意初始化
if(op==3)Ans=1e9;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
if(op==1)Ans+=Query(1,1,n,dfn[top[x]],dfn[x],op);
if(op==2)Ans=max(Ans,Query(1,1,n,dfn[top[x]],dfn[x],op));
if(op==3)Ans=min(Ans,Query(1,1,n,dfn[top[x]],dfn[x],op));
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
if(x==y)return Ans;
if(op==1)Ans+=Query(1,1,n,dfn[x]+1,dfn[y],op);
if(op==2)Ans=max(Ans,Query(1,1,n,dfn[x]+1,dfn[y],op));
if(op==3)Ans=min(Ans,Query(1,1,n,dfn[x]+1,dfn[y],op));
return Ans;
}
int main()
{
n=read();
for(int i=1;i<n;i++)
{
int a=read(),b=read(),c=read();
insert(a,b,c);
}
dep[0]=1;
dfs1(0);
dfs2(0,0);
Build(1,1,n);
m=read();
RG char act[5];
RG int u,v;
while(m--)
{
scanf("%s",act);
if(act[0]==‘C‘){u=read();v=read();Modify_Point(1,1,n,dfn[u],v);};
if(act[0]==‘N‘){u=read();v=read();Modify_Opp(u,v);};
if(act[0]==‘S‘){u=read();v=read();printf("%d\n",Query_Tree(u,v,1));}
if(act[1]==‘A‘){u=read();v=read();printf("%d\n",Query_Tree(u,v,2));}
if(act[1]==‘I‘){u=read();v=read();printf("%d\n",Query_Tree(u,v,3));}
}
return 0;
}