题解:
方法一 我们采用树链剖分+线段树的做法
对于操作2,3都是基本的树剖操作 唯一不同的是换根 对于换根我们分情况讨论
若查询的节点是当前根的lca节点 那么查询的是 查询节点在根-节点路径上的儿子节点的子树的补集 (可以手画一下
若查询的节点不是lca那么直接查询原树的子树范围
若查询的节点是当前根 则查询整颗子树 都是树剖基本操作
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const int MAXN=3e5+10;
const double eps=1e-8;
#define ll long long
const int inf=2147483647;
using namespace std;
struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
int fa[MAXN][21],dep[MAXN],son[MAXN],num[MAXN],n,m,a[MAXN];
void dfs1(int x,int pre,int deep){
dep[x]=deep+1;fa[x][0]=pre;num[x]=1;
link(x){
if(j->t==pre)continue;
dfs1(j->t,x,deep+1);
num[x]+=num[j->t];
if(son[x]==-1||num[son[x]]<num[j->t])son[x]=j->t;
}
}
int tp[MAXN],p[MAXN],cnt,fp[MAXN];
void dfs2(int x,int td){
tp[x]=td;p[x]=++cnt;fp[p[x]]=x;
inc(i,1,20)fa[x][i]=fa[fa[x][i-1]][i-1];
if(son[x]!=-1)dfs2(son[x],td);
link(x){
if(j->t==fa[x][0]||j->t==son[x])continue;
dfs2(j->t,j->t);
}
}
int Lca(int u,int v){
int uu=tp[u];int vv=tp[v];
while(uu!=vv){
if(dep[uu]<dep[vv])swap(uu,vv),swap(u,v);
u=fa[uu][0];uu=tp[u];
}
if(dep[u]>dep[v])swap(u,v);
return u;
}
int Clca(int u,int v){
dec(i,20,0){
if(dep[fa[u][i]]>dep[v])u=fa[u][i];
}
return u;
}
int minn[MAXN<<2],flag[MAXN<<2];
void push(int x){
if(flag[x]){
minn[x<<1]=minn[x<<1|1]=flag[x];
flag[x<<1]=flag[x<<1|1]=flag[x];
flag[x]=0;
}
}
void up(int x){minn[x]=min(minn[x<<1],minn[x<<1|1]);}
void built(int rt,int l,int r){
flag[rt]=0;
if(l==r){minn[rt]=a[fp[l]];return ;}
int mid=(l+r)>>1;
built(rt<<1,l,mid);
built(rt<<1|1,mid+1,r);
up(rt);
}
void update(int rt,int l,int r,int ql,int qr,int k){
if(ql<=l&&r<=qr){flag[rt]=minn[rt]=k;return ;}
int mid=(l+r)>>1;
push(rt);
if(ql<=mid)update(rt<<1,l,mid,ql,qr,k);
if(qr>mid)update(rt<<1|1,mid+1,r,ql,qr,k);
up(rt);
}
int ans;
void query(int rt,int l,int r,int ql,int qr){
if(ql>qr)return ;
if(ql<=l&&r<=qr){ans=min(ans,minn[rt]);return ;}
int mid=(l+r)>>1;
push(rt);
if(ql<=mid)query(rt<<1,l,mid,ql,qr);
if(qr>mid)query(rt<<1|1,mid+1,r,ql,qr);
up(rt);
}
void operator1(int u,int v,int k){
int uu=tp[u];int vv=tp[v];
while(uu!=vv){
if(dep[uu]<dep[vv])swap(u,v),swap(uu,vv);
update(1,1,n,p[uu],p[u],k);
u=fa[uu][0];uu=tp[u];
}
if(dep[u]>dep[v])swap(u,v);
update(1,1,n,p[u],p[v],k);
}
int main(){
minn[0]=inf;
n=read();m=read();
int u,v,k,op;
inc(i,2,n)u=read(),v=read(),add(u,v),add(v,u);
inc(i,1,n)a[i]=read(),son[i]=-1;
dfs1(1,0,0);dfs2(1,1);
built(1,1,n);
int Rt=read();
while(m--){
op=read();u=read();
if(op==1)Rt=u;
else if(op==2)v=read(),k=read(),operator1(u,v,k);
else{
if(u==Rt){printf("%d\n",minn[1]);continue;}
int lca=Lca(Rt,u);
if(lca!=u){
ans=inf;query(1,1,n,p[u],p[u]+num[u]-1);
printf("%d\n",ans);
}
else{
v=Clca(Rt,u);
ans=inf;
query(1,1,n,1,p[v]-1);
query(1,1,n,p[v]+num[v],n);
printf("%d\n",ans);
}
}
}
return 0;
}
方法二: 采用LCT 用multiset维护虚子树信息然后解决即可 因为复杂度是O(nlog^2n)常数较大 在oj上没有通过 本地对拍AC 拿来练手
#include <bits/stdc++.h>
const int MAXN=1e5+10;
#define ll long long
const int inf=2147483647;
using namespace std;
ll readll(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int readint(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
int ch[MAXN][2],minn[MAXN],key[MAXN],pre[MAXN],res[MAXN],flag[MAXN],cminn[MAXN];
bool rt[MAXN];
multiset<int>s[MAXN];
void reverse(int r){
if(!r) return ;
swap(ch[r][0],ch[r][1]);
res[r]^=1;
}
void Flag(int r,int t){
if(!r)return ;
key[r]=t;
//cout<<t<<" "<<(*s[r].begin())<<endl;
minn[r]=min(t,cminn[r]);
flag[r]=t;
}
void push(int r){
if(res[r]){
reverse(ch[r][0]);
reverse(ch[r][1]);
res[r]^=1;
}
if(flag[r]){
Flag(ch[r][0],flag[r]);
Flag(ch[r][1],flag[r]);
flag[r]=0;
}
}
void up(int x){
minn[x]=min(key[x],min(minn[ch[x][0]],minn[ch[x][1]]));
cminn[x]=min(*s[x].begin(),cminn[ch[x][0]]);
cminn[x]=min(cminn[x],cminn[ch[x][1]]);
// cout<<key[x]<<" "<<x<<" "<<minn[ch[x][0]]<<" "<<minn[ch[x][1]]<<endl;
minn[x]=min(minn[x],*s[x].begin());
//cout<<minn[x]<<endl;
}
void P(int r){
if(!rt[r]) P(pre[r]);
push(r);
}
void rotate(int x,int kind){
int y=pre[x];
pre[ch[x][kind]]=y;ch[y][!kind]=ch[x][kind];
if(rt[y]) rt[y]=0,rt[x]=1;
else ch[pre[y]][ch[pre[y]][1]==y]=x;
pre[x]=pre[y];ch[x][kind]=y;pre[y]=x;
up(y);
}
void splay(int x){
P(x);
while(!rt[x]){
if(rt[pre[x]]) rotate(x,ch[pre[x]][0]==x);
else{
int y=pre[x];int kind=ch[pre[y]][0]==y;
if(ch[y][kind]==x) rotate(x,!kind),rotate(x,kind);
else rotate(y,kind),rotate(x,kind);
}
}
up(x);
}
void access(int x){
int y=0;
while(x){
splay(x);
if(ch[x][1]) s[x].insert(minn[ch[x][1]]),rt[ch[x][1]]=1,pre[ch[x][1]]=x;
if(y) rt[y]=0,s[x].erase(lower_bound(s[x].begin(),s[x].end(),minn[y]));
ch[x][1]=y;
up(x);
y=x;x=pre[x];
}
}
void mroot(int r){
access(r);splay(r);
reverse(r);
}
void update(int u,int v,int t){
mroot(u);access(v);splay(v);
//key[v]=t;
Flag(v,t);
//up(v);
//cout<<minn[v]<<" "<<endl;
}
void Link(int u,int v){
//cout<<minn[u]<<" "<<minn[v]<<" "<<u<<" "<<v<<endl;
mroot(u);mroot(v);//cout<<minn[u]<<" "<<minn[v]<<endl;
pre[u]=v;
s[v].insert(minn[u]);
up(v);
//cout<<minn[v]<<endl;
}
int querty(int v){
//cout<<v<<" "<<pre[v]<<endl;
access(v);splay(v);
//cout<<ch[v][0]<<" "<<ch[v][1]<<endl;
//cout<<(*s[v].begin())<<endl;
//cout<<key[v]<<endl;
return min(key[v],(*s[v].begin()));
}
void newnode(int t){
ch[t][0]=ch[t][1]=pre[t]=res[t]=0;rt[t]=1;
}
typedef struct Node{
int x,y;
}Node;
Node que[MAXN];
int main(){
//ios::sync_with_stdio(false);
int n,m;n=readint();m=readint();
int f,vul;
cminn[0]=key[0]=minn[0]=inf;
//for(int i=1;i<=n;i++) newnode(i),s[i].insert(inf);
for(int i=1;i<n;i++){
que[i].x=readint();
que[i].y=readint();
// f=readint();vul=readint();key[i]=minn[i]=vul;
//cout<<key[i]<<" "<<minn[i]<<endl;
//if(f==0) continue;
// Link(i,f);
}
for(int i=1;i<=n;i++)newnode(i),s[i].insert(inf),cminn[i]=inf,key[i]=minn[i]=readint();
for(int i=1;i<n;i++)Link(que[i].x,que[i].y);
int Rt=readint();
//cout<<querty(3)<<endl;
int op;int x,y,k;
for(int i=1;i<=m;i++){
//scanf(" %c",&ch);
op=readint();
if(op==1){
x=readint();Rt=x;
//x=readint();y=readint();update(x,y);
}
else if(op==2){
x=readint();y=readint();k=readint();
update(x,y,k);
//x=readint();mroot(x);
}
else x=readint(),mroot(Rt),printf("%d\n",querty(x));
//for(int i=1;i<=n;i++) cout<<querty(i)<<" ";
// cout<<endl;
}
return 0;
}
3083: 遥远的国度
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 5121 Solved: 1484
[Submit][Status][Discuss]
Description
描述
zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。
问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。
Input
第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为根的子树中的最小防御值。
Output
对于每个opt=3的操作,输出一行代表对应子树的最小点权值。
Sample Input
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
Sample Output
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。