[BZOJ]3083: 遥远的国度

题解:  

方法一  我们采用树链剖分+线段树的做法

对于操作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 MB
Submit: 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

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

Sample Output

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。

 

posted @ 2019-02-03 20:53  wang9897  阅读(171)  评论(0编辑  收藏  举报