[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 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。