[国家集训队]旅游

Posted faced

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[国家集训队]旅游相关的知识,希望对你有一定的参考价值。

[国家集训队]旅游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;
}

以上是关于[国家集训队]旅游的主要内容,如果未能解决你的问题,请参考以下文章

P1505 [国家集训队]旅游(树剖板题)

[国家集训队]旅游

P1505 [国家集训队]旅游

p1505[国家集训队]旅游

[国家集训队]旅游

P1505 [国家集训队]旅游树链剖分