• 题解 P1505 【[国家集训队]旅游】


    [ ext{关于题意} ]

    (quad)一道很简单毒瘤的树链剖分练习题,只有五种操作,除了码量较大,就没有什么其他特点了,是一道很好的练习题。可以锻炼(OIer)找错能力,我调了一个小时, (WA)了一次

    [ ext{关于边权转点权} ]

    (quad)有几道题也是树链剖分边权转点权,可以先去做一下:P4114 Qtree1P3950 部落冲突(我的题解),CF165D Beard Graph(我的题解)。

    (quad)考虑到树上有 (n) 个点,有 (n-1) 条边,并且每个点有且只有一个父亲节点(除了根节点),但是每个点可能有多个儿子(也可能没有),所以我们可以将边权转化为儿子节点的点权(深度更大的点),这样除了根节点,(n-1) 条边就合理的分配到 (n-1) 个点上。(如果还不能就看看图吧)

    原图

    经过转化后的图(将边权转化为点权)

    (quad)注意:对于路径(4-2-5),只需访问点 (4) 和点 (5),对于 (4)(5)(LCA) (最近公共祖先)不可取,因为 (2) 在原图中对应的是边 (1-2),并不在路径(4-2-5)上,所以在树链剖分中当 (x)(y) 在同一条链上时((dep[x]<dep[y])),只需询问 (x+1)(y) 的路径。

    il void dfs1(int x,int fa,int ss) //ss表示x与fa(x的父亲)的连边的权值
    {
      father[x]=fa;dep[x]=dep[fa]+1;size[x]=1;a[x]=ss;
      for(re i=head[x],y;i,y=go[i];i=next[i])
        {
          if(y==fa)continue;
          dfs1(y,x,s[i]);
          size[x]+=size[y];
          if(size[y]>size[son[x]])son[x]=y;
        }
    }
    
    il int query1(int x,int y,int z)
    {
      int fx=top[x],fy=top[y],res;
      if(z==1)res=0;
      else if(z==2)res=-10000;
      else if(z==3)res=10000;
      while(fx!=fy)
        {
          if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
          if(z==1)res+=query(1,1,n,seg[fx],seg[x],z);
          else if(z==2)res=max(res,query(1,1,n,seg[fx],seg[x],z));
          else if(z==3)res=min(res,query(1,1,n,seg[fx],seg[x],z));
          x=father[fx];fx=top[x];
        }
      if(dep[x]>dep[y])swap(x,y);
      if(seg[x]==seg[y])return res;
      if(z==1)res+=query(1,1,n,seg[x]+1,seg[y],z);//最后一次询问是seg[x]+1到seg[y]
      if(z==2)res=max(res,query(1,1,n,seg[x]+1,seg[y],z));//最后一次询问是seg[x]+1到seg[y]
      if(z==3)res=min(res,query(1,1,n,seg[x]+1,seg[y],z));//最后一次询问是seg[x]+1到seg[y]
      return res;
    }
    

    [ ext{对于五种操作} ]

    1. 操作1:单点修改,输入 (C) (i) (w) 将输入的第 (i) 条边权值改为 (w) ,找出第 (i) 条边的两个端点,修改儿子节点
    il void change3(int x,int y)
    {
      int xx=go[x<<1],yy=go[(x<<1)-1];
      if(xx==father[yy])change1(1,1,n,seg[yy],y);
      else change1(1,1,n,seg[xx],y);
    }
    
    1. 操作2:区间修改,输入 (N) (u) (v)(u,v) 节点之间的边权都变为相反数,用布尔数组 (c[k]) (懒标记)记录k区间内是否要被修改,每次修改 (c[k])(^)$ =1$;

    2. 操作3:区间询问,输入 (SUM) (u) (v) 询问 (u,v) 节点之间边权和

    3. 操作4:区间询问,输入 (MAX) (u) (v) 询问 (u,v) 节点之间边权最大值

    4. 操作5:区间询问,输入 (MIN) (u) (v) 询问 (u,v) 节点之间边权最小值

    (quad)其他就是一些细节方面的问题了,比如取反时 (sum[k]=-sum[k])(Min[k]=-Max[k])(Max[k]=-Min[k])

    il void change(int k,int l,int r)
    {
        int m1=Min[k],m2=Max[k];
        sum[k]=-sum[k];Min[k]=-m2;Max[k]=-m1;c[k]^=1;
    }
    

    接下来就是完整(AC)代码了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    using namespace std;
    #define next neee
    #define re register int
    #define il inline
    #define inf 1e18
    il int read()
    {
    	int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)&&ch!='-')ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
        return x*f;
    }
    il void print(int x)
    {if(x<0)putchar('-'),x=-x;if(x/10)print(x/10);putchar(x%10+'0');}
    const int N=2e5+5;
    int n,m,next[N<<1],go[N<<1],head[N],tot,s[N<<1],a[N],Min[N<<2],top[N];
    int sum[N<<2],Max[N<<2],seg[N],rev[N],son[N],size[N],dep[N],father[N];
    bool c[N<<2];
    il void Add(int x,int y,int z)//链式前向星存图
    {next[++tot]=head[x];head[x]=tot;go[tot]=y;s[tot]=z;}
    //------------预处理---------------------------
    il void dfs1(int x,int fa,int ss)//第一次dfs处理数组father,son,dep,size,a
    {
      father[x]=fa;dep[x]=dep[fa]+1;size[x]=1;a[x]=ss;//ss表示x与fa(x的父亲)的连边的权值
      for(re i=head[x],y;i,y=go[i];i=next[i])
        {
          if(y==fa)continue;
          dfs1(y,x,s[i]);
          size[x]+=size[y];
          if(size[y]>size[son[x]])son[x]=y;
        }
    }
    il void dfs2(int x,int topf)//第二次dfs处理数组top,seg,rev
    {
      top[x]=topf;seg[x]=++seg[0];rev[seg[x]]=x;
      if(!son[x])return;
      dfs2(son[x],topf);
      for(re i=head[x],y;i,y=go[i];i=next[i])
        {
          if(top[y])continue;
          dfs2(y,y);
        }
    }
    il void build(int k,int l,int r)//建树
    {
      if(l==r){Min[k]=Max[k]=sum[k]=a[rev[l]];return;}
      int mid=l+r>>1;
      build(k<<1,l,mid);build(k<<1|1,mid+1,r);
      Min[k]=min(Min[k<<1],Min[k<<1|1]);
      Max[k]=max(Max[k<<1],Max[k<<1|1]);
      sum[k]=sum[k<<1]+sum[k<<1|1];
    }
    //------------预处理---------------------------
    //------------线段树---------------------------
    il void change(int k,int l,int r)//取反操作
    {int m1=Min[k],m2=Max[k];sum[k]=-sum[k];Min[k]=-m2;Max[k]=-m1;c[k]^=1;}
    il void pushdown(int k,int l,int r,int mid)//下传标记
    {if(l==r){c[k]=0;return;}change(k<<1,l,mid);change(k<<1|1,mid+1,r);c[k]=0;}
    il void change1(int k,int l,int r,int x,int y)//单点修改
    {
      if(l==r){Min[k]=Max[k]=sum[k]=y;return;}
      int mid=l+r>>1;
      if(c[k])pushdown(k,l,r,mid);
      if(x<=mid)change1(k<<1,l,mid,x,y);
      else change1(k<<1|1,mid+1,r,x,y);
      Min[k]=min(Min[k<<1],Min[k<<1|1]);
      Max[k]=max(Max[k<<1],Max[k<<1|1]);
      sum[k]=sum[k<<1]+sum[k<<1|1];
    }
    il void change2(int k,int l,int r,int x,int y)//区间取反
    {
      if(x<=l&&y>=r){change(k,l,r);return;}
      int mid=l+r>>1;
      if(c[k])pushdown(k,l,r,mid);
      if(x<=mid)change2(k<<1,l,mid,x,y);
      if(y>mid)change2(k<<1|1,mid+1,r,x,y);
      Min[k]=min(Min[k<<1],Min[k<<1|1]);
      Max[k]=max(Max[k<<1],Max[k<<1|1]);
      sum[k]=sum[k<<1]+sum[k<<1|1];
    }
    il int query(int k,int l,int r,int x,int y,int z)//区间询问
    {
      if(x<=l&&y>=r){
        if(z==1)return sum[k];//z==1-->>求SUM
        if(z==2)return Max[k];//z==2-->>求MAX
        return Min[k];}		//z==3-->>求MIN
      int mid=l+r>>1,res;
      if(c[k])pushdown(k,l,r,mid);
      if(z==1){res=0;if(x<=mid)res+=query(k<<1,l,mid,x,y,z);if(y>mid)res+=query(k<<1|1,mid+1,r,x,y,z);return res;}
      if(z==2){res=-10000;if(x<=mid)res=max(res,query(k<<1,l,mid,x,y,z));if(y>mid)res=max(res,query(k<<1|1,mid+1,r,x,y,z));return res;}
      res=10000;if(x<=mid)res=min(res,query(k<<1,l,mid,x,y,z));if(y>mid)res=min(res,query(k<<1|1,mid+1,r,x,y,z));return res;
    }
    //------------线段树--------------------------
    //------------树链剖分------------------------
    il void change3(int x,int y)
    {
      int xx=go[x<<1],yy=go[(x<<1)-1];
      if(xx==father[yy])change1(1,1,n,seg[yy],y);
      else change1(1,1,n,seg[xx],y);
    }
    il void change4(int x,int y)
    {
      int fx=top[x],fy=top[y];
      while(fx!=fy){
        if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
        change2(1,1,n,seg[fx],seg[x]);	//链顶在前
        x=father[fx];fx=top[x];
      }
      if(dep[x]>dep[y])swap(x,y);
      if(seg[x]+1<=seg[y])change2(1,1,n,seg[x]+1,seg[y]);//记得seg[x]+1
    }
    il int query1(int x,int y,int z)
    {
      int fx=top[x],fy=top[y],res;
      if(z==1)res=0;
      else if(z==2)res=-10000;
      else if(z==3)res=10000;
      while(fx!=fy)
        {
          if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
          if(z==1)res+=query(1,1,n,seg[fx],seg[x],z);
          else if(z==2)res=max(res,query(1,1,n,seg[fx],seg[x],z));
          else if(z==3)res=min(res,query(1,1,n,seg[fx],seg[x],z));
          x=father[fx];fx=top[x];
        }
      if(dep[x]>dep[y])swap(x,y);
      if(seg[x]==seg[y])return res;
      if(z==1)res+=query(1,1,n,seg[x]+1,seg[y],z);//最后一次询问是seg[x]+1到seg[y]
      if(z==2)res=max(res,query(1,1,n,seg[x]+1,seg[y],z));//最后一次询问是seg[x]+1到seg[y]
      if(z==3)res=min(res,query(1,1,n,seg[x]+1,seg[y],z));//最后一次询问是seg[x]+1到seg[y]
      return res;
    }
    //------------树链剖分------------------------
    signed main()
    {
      n=read();
      for(re i=1;i<n;i++)
      {re x=read()+1,y=read()+1,z=read();Add(x,y,z);Add(y,x,z);}
      m=read();
      dfs1(1,0,0);dfs2(1,1);build(1,1,n);//树链剖分预处理
      string s1;
      while(m--)
        {
          cin>>s1;re x=read()+1,y=read()+1,z=0;
          if(s1=="C"){change3(x-1,y-1);continue;}
          if(s1=="N"){change4(x,y);continue;}
          if(s1=="SUM")z=1;else if(s1=="MAX")z=2;else if(s1=="MIN")z=3;
          print(query1(x,y,z));putchar('
    ');
        }
      return 0;
    }
    

    (quad)写题解不易,如果觉得对您有帮助,不妨点个赞吧!

  • 相关阅读:
    mysql,windows自动备份设置
    彻底搞清楚javascript中的require、import和export
    Spring Boot 打包报错Failed to execute goal org.apache.maven.plugins:mavenresourcesplugin:3.2.0
    Spring AOP 切点切面
    12.5M 30M 90M DEM免费下载!【转】
    JS 中的数组遍历方式效率比较[转]
    cesium加载CAD模型(.dwg)
    Cesium发布下一代3D Tiles规范预览
    cesium点击面高亮事件[转]
    MySQL 5.7及8.0版本数据库的root密码遗忘的解决办法
  • 原文地址:https://www.cnblogs.com/FarkasW/p/13924544.html
Copyright © 2020-2023  润新知