遥远的国度(树链剖分,换根)

遥远的国度

题目描述

zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。

问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。

RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。

由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。

输入输出格式

输入格式:

第1行两个整数n m,代表城市个数和操作数。

第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。

第n+1行,有n个整数,代表所有点的初始防御值。

第n+2行一个整数 id,代表初始的首都为id。

第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。

输出格式:

对于每个opt=3的操作,输出一行代表对应子树的最小点权值。

输入输出样例

输入样例#1: 复制

3 7
1 2
1 3
1 2 3
1
3 1
2 1 1 6
3 1
2 2 2 5
3 1
2 3 3 4
3 1

输出样例#1: 复制

1
2
3
4

说明

对于20%的数据,n<=1000 m<=1000。

对于另外10%的数据,n<=100000,m<=100000,保证修改为单点修改。

对于另外10%的数据,n<=100000,m<=100000,保证树为一条链。

对于另外10%的数据,n<=100000,m<=100000,没有修改首都的操作。

对于100%的数据,n<=100000,m<=100000,0<所有权值<=2^31。

题解

终于填了树链剖分换根这个坑了。
就是换根的时候的我们要分类讨论而不是真的去换根。
有三种情况。

第一种就是询问的点就是当前根。
那么直接输出整棵树的最小值就可以了。

第二种就是当前根在询问的点的子树内。
那么我们首先可以发现。
假如当前根就是询问点的直接连边的儿子的话。
就相当于在询问点的子树去掉当前根的子树求值。
那么多次换根就等于询问的点到当前根的路径上的所有子树都去掉。
而这些子树又奇妙的全包括在询问的点到当前根的路径上的第一个节点。只要把这个点求出来并去掉其子树就可以了。

第三种就是当前根不在询问的点的子树内。
那么它的子树还是原来的子树。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1000001;
const int inf=(1ll<<31)-1;
struct node{
    int to,nex;
}e[N];
int num,head[N],sum[N],lazy[N];
int n,m,root,dep[N],a[N],top[N];
int fa[N],son[N],size[N],pos[N],ch[N];
int f[N][21],tot;
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

void add(int from,int to){
    num++;
    e[num].to=to;
    e[num].nex=head[from];
    head[from]=num;
}

void build(int root,int left,int right){
    if(left==right){
        sum[root]=a[left];
        return ;
    }
    int mid=(left+right)>>1;
    build(root<<1,left,mid);build(root<<1|1,mid+1,right);
    sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

void push(int root,int left,int right){
    lazy[root<<1]=lazy[root<<1|1]=lazy[root];
    sum[root<<1]=sum[root<<1|1]=lazy[root];
    lazy[root]=0;
}

void update(int root,int left,int right,int l,int r,int v){
    if(left>r||right<l)return ;
    if(left>=l&&right<=r){
        lazy[root]=sum[root]=v;
        return ;
    }
    if(lazy[root])push(root,left,right);
    int mid=(left+right)>>1;
    if(mid>=l) update(root<<1,left,mid,l,r,v);
    if(mid<r)  update(root<<1|1,mid+1,right,l,r,v);
    sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

int query(int root,int left,int right,int l,int r){
    if(left>r||right<l)return inf;
    if(lazy[root])push(root,left,right);
    if(left>=l&&right<=r)return sum[root];
    int mid=(left+right)>>1;int ans1=inf,ans2=inf;
    if(mid>=l) ans1=query(root<<1,left,mid,l,r);
    if(mid<r)  ans2=query(root<<1|1,mid+1,right,l,r);
    return min(ans1,ans2);
}

void dfs1(int x){
    size[x]=1;
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(!dep[v]){
            dep[v]=dep[x]+1;fa[v]=x;
            f[v][0]=x;
            dfs1(v);size[x]+=size[v];
            if(size[son[x]]<size[v])son[x]=v;
        }
    }
}

void dfs2(int x,int tp){
    pos[x]=++tot;a[tot]=ch[x];top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v!=fa[x]&&v!=son[x])dfs2(v,v);
    }
}

void init(){
    for(int i=1;i<=17;i++)
        for(int j=1;j<=n;j++)
        f[j][i]=f[f[j][i-1]][i-1];
}

void cal(int x,int y,int v){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
        update(1,1,n,pos[fx],pos[x],v);
        x=fa[fx];fx=top[x];
    }
    if(dep[x]>dep[y])swap(x,y);
    update(1,1,n,pos[x],pos[y],v);
}

int cal2(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);
        x=fa[fx];fx=top[x];
    }
    if(dep[x]>dep[y])return y;
    return x;
}

int find(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    if(f[x][0]==y)return x;
    for(int j=17;j>=1;j--)if(dep[f[x][j]]>=dep[y])x=f[x][j];
    if(f[x][0]==y)return x;
    for(int j=17;j>=0;j--)if(f[x][j]!=f[y][j])x=f[x][j],y=f[y][j];
    return x;
}

int main(){
    n=read();m=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)ch[i]=read();
    root=read();
    fa[root]=root;dep[root]=1;
    dfs1(root);dfs2(root,root);build(1,1,n);init();
    for(int i=1;i<=m;i++){
        int opt=read();
        if(opt==1)root=read();
        if(opt==2){int x=read(),y=read(),z=read();cal(x,y,z);}
        if(opt==3){
            int x=read();int lca=cal2(x,root);
            if(root==x){printf("%d\n",query(1,1,n,1,n));continue;}
            if(lca!=x){printf("%d\n",query(1,1,n,pos[x],pos[x]+size[x]-1));continue;}
            if(lca==x){
                int LCA=root;
                for(int i=17;i>=0;i--)
                if(dep[f[LCA][i]]>dep[x])LCA=f[LCA][i];
                int ans=min(query(1,1,n,1,pos[LCA]-1),query(1,1,n,pos[LCA]+size[LCA],n));
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hhh1109/p/9573257.html

时间: 2024-10-06 00:29:22

遥远的国度(树链剖分,换根)的相关文章

BZOJ 3083 遥远的国度 树链剖分

3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 797  Solved: 181[Submit][Status] Description 描述 zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀. 问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连

BZOJ 3083 遥远的国度 树链剖分+线段树

有换根的树链剖分的裸题. 在换根的时候注意讨论. 注意数据范围要开unsigned int或longlong #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> #include<string> #include<iomanip> #include<a

洛谷 P3979 遥远的国度(树链剖分)

题目描述 修改某条路径上的值以及询问子树的最小值都是最树剖的基础操作,那么如何实现换根呢? 考虑一下三种情况: 1.rot=询问的子树x,答案就是整棵树的最小值 2.rot在x的子树里,只有rot到x这一条链上的的节点的子树会变 找到x在rot方向上的子节点,答案就是除去这棵子树的最小值 3.rot不在x的子树里,那么rot是谁对x的子树没有影响,答案不变 那么就在询问时分类讨论一下就好了 #include<complex> #include<cstdio> using names

树链剖分学习笔记

先让我们看一个题目 有一棵n个节点的树,树的每条边有个边权,有如下两种操作 1.修改一条边的边权 2.查询两点之间路径的权值 对于这种题目,可能有人会选择直接暴力,这很明显不行. 换一种思路,如果我们把树的每一条边拆下来,对他们进行编号,然后使用线段树来存储呢?使用线段树来对每条边的边权进行修改和查询是很方便的.于是这样我们就引出了树链剖分. 树链剖分其实就是把一棵树上的各个边拆开来进行处理,从而对树的整体进行划分. 将树划分为链,用数据结构来维护这些链,时间复杂度大致为O(log n) . 在

树链剖分模板+入门题 SPOJ - QTREE

题目链接:[点击进入](http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=13013) 树链剖分并不是一个复杂的算法或者数据结构,只是能把一棵树拆成链来处理而已,换一种说法,树链剖分只是xxx数据结构/算法在树上的推广,或者说,树链剖分只是把树hash到了几段连续的区间上.比如说下面这道题,就是将树分为重链和轻链然后映射到线段树上,然后再在线段树上进行查询和修改等操作.所以树链剖分的重点有两个,一是正确的将树分解成几段并映射到

【BZOJ1146】【树链剖分+平衡树】网络管理Network

Description M 公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通 信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信 子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由

LibreOJ #139 树链剖分 [树链剖分,线段树]

题目传送门 树链剖分 题目描述 这是一道模板题. 给定一棵 nnn 个节点的树,初始时该树的根为 111 号节点,每个节点有一个给定的权值.下面依次进行 mmm 个操作,操作分为如下五种类型: 换根:将一个指定的节点设置为树的新根. 修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值. 修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值. 询问路径:询问某条路径上节点的权值和. 询问子树:询问某个子树内节点的权值和. 输入

树链剖分——树形到线性的转化

树链剖分: 树上操作并不能实现一段链的直接更新. 树链剖分就解决了这个问题. 本质上是树形到线性的转化. 通过子树,重链是一个连续的dfn区间的优秀性质,可以在dfn序列上进行操作,达到在树上操作的目的. 通常和线段树结合. 板子:以前写的. 树链剖分 例题: 1.遥远的国度 题目大意: 给定一棵有根树,每个点有一个权值,提供三种操作: 1.将x节点变为根节点 2.将x到y路径上的点的权值全部改为v 3.询问x的子树中点权的最小值 如果根不变,那么2.3就直接做了. 但是根变化了,随之第三问,子

[bzoj4712] 洪水 [树链剖分+线段树+dp]

题面 传送门 思路 DP方程 首先,这题如果没有修改操作就是sb题,dp方程如下 $dp[u]=max(v[u],max(dp[v]))$,其中$v$是$u$的儿子 我们令$g[u]=max(dp[v])$ 修改? 我们发现,本题中所有的修改都是非负的 也就是说,每一次修改结束以后,$dp[u]$的值只增不减 同时,修改$u$位置的$v[u]$值,只会影响到$u$到根的这一条链上的$dp$值 我们考虑修改后,这条链上的每个点的$dp[u]$值的增量,令这个增量为$delta[u]$ 那么显然当$