cf 487E/uoj 30 Tourists
题目描述
codeforces: http://codeforces.com/contest/487/problem/E
uoj: http://uoj.ac/problem/30
题解
学习了圆方树。
我们定义原图上的点为圆点,定义点双对应的新点为方点。
对于一张图,我们把它缩点双,每个点双新开一个方点,并向点双内的所有圆点连边。
显然这样搞完之后原图会变成一棵树,而且每个方点的所有相邻节点都是圆点,每个圆点所有的相邻节点都是方点。
对于每两个点,他们之间的的简单路径可能经过的点就是它们在圆方树上的最短路径上经过的所有方点所代表的点双所对应的圆点。
为了方便这道题的修改操作,每个方点只维护它的所有孩子的信息,不维护它的父亲那个圆点。
在查询的时候,如果lca是方点就再加上他的父亲那个圆点的信息就好了。
每个方点开个堆来维护最小值就行了。
代码
#include<bits/stdc++.h>
#define inf 2100000000
#define N 400010
using namespace std;
int n,m,Q,w[N],cnt,dfn[N],low[N],tot,blo[N];
int fa[N],top[N],pos[N],po[N],dep[N],size[N],nxt[N];
int k,la[N],ff[N],tp,q[N],flag[N];
struct node{int a,b;}e[N];
struct info{
int x,y;
bool operator<(const info &p)const{return y>p.y;}
};
struct data{int minn;}t[N*4];
vector<int>s[N];
priority_queue<info>h[N];
void add(int a,int b)
{
e[++k]=(node){a,b};ff[k]=la[a];la[a]=k;
e[++k]=(node){b,a};ff[k]=la[b];la[b]=k;
}
void insert(int x,int p)
{
if(blo[x]==p)return;
s[p].push_back(x);blo[x]=p;
}
void dfs(int x,int pre)
{
dfn[x]=++cnt;low[x]=cnt;
for(int a=la[x];a;a=ff[a])
{
if(!dfn[e[a].b])
{
q[++tp]=a;dfs(e[a].b,x);
low[x]=min(low[x],low[e[a].b]);
if(low[e[a].b]>=dfn[x])
{
tot++;
for(;tp;tp--)
{
insert(e[q[tp]].a,tot);
insert(e[q[tp]].b,tot);
if(q[tp]==a){tp--;break;}
}
}
}
else if(dfn[e[a].b]<dfn[x]&&e[a].b!=pre)
{
q[++tp]=a;low[x]=min(low[x],dfn[e[a].b]);
}
}
}
//点双
void solve()
{
k=0;memset(la,0,sizeof(la));
for(int i=1;i<=tot;i++)
for(int j=0;j<s[i].size();j++)add(n+i,s[i][j]);
}
//构建圆方树
void bfs()
{
int l=1,r=2;q[1]=1;dep[1]=1;cnt=0;
while(l<r)
{
int x=q[l++];size[x]=1;
for(int a=la[x];a;a=ff[a])
{
if(dep[e[a].b])continue;
q[r]=e[a].b;fa[q[r]]=x;dep[q[r]]=dep[x]+1;r++;
}
}
for(int i=1;i<=n;i++)
if(fa[i])h[fa[i]].push((info){i,w[i]});
for(int i=r-1;i;i--)
{
int x=q[i];if(!fa[x])continue;
size[fa[x]]+=size[x];
if(size[nxt[fa[x]]]<size[x])nxt[fa[x]]=x;
}
for(int i=1;i<r;i++)
{
int x=q[i];if(top[x])continue;
for(int j=x;j;j=nxt[j])top[j]=x,po[pos[j]=++cnt]=j;
}
}
int get(int x)
{
if(x<=n)return w[x];
while(1)
{
info p=h[x].top();
if(w[p.x]==p.y)return p.y;
h[x].pop();
}
}
class seg_tree
{
public:
void build(int x,int l,int r)
{
if(l==r){t[x].minn=get(po[l]);return;}
int mid=l+r>>1,lc=x<<1,rc=lc+1;
build(lc,l,mid);build(rc,mid+1,r);
t[x].minn=min(t[lc].minn,t[rc].minn);
}
int qry(int x,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return t[x].minn;
int mid=l+r>>1,lc=x<<1,rc=lc+1,res=inf;
if(ql<=mid)res=min(res,qry(lc,l,mid,ql,qr));
if(qr>mid)res=min(res,qry(rc,mid+1,r,ql,qr));
return res;
}
void modify(int x,int l,int r,int pos,int a,int b)
{
if(l==r){
if(po[l]>n)h[po[l]].push((info){a,b});
t[x].minn=get(po[l]);return;
}
int mid=l+r>>1,lc=x<<1,rc=lc+1;
if(pos<=mid)modify(lc,l,mid,pos,a,b);
else modify(rc,mid+1,r,pos,a,b);
t[x].minn=min(t[lc].minn,t[rc].minn);
}
}T;
int qry(int x,int y)
{
int res=inf;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
res=min(res,T.qry(1,1,cnt,pos[top[x]],pos[x]));
x=fa[top[x]];
}
if(pos[x]>pos[y])swap(x,y);
res=min(res,T.qry(1,1,cnt,pos[x],pos[y]));
if(x>n)res=min(res,w[fa[x]]);
return res;
}
int main()
{
int a,b;char ch;
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<=m;i++)
scanf("%d%d",&a,&b),add(a,b);
dfs(1,0);solve();bfs();
T.build(1,1,cnt);
while(Q--)
{
scanf(" %c%d%d",&ch,&a,&b);
if(ch=='A')printf("%d\n",qry(a,b));
else
{
if(w[a]==b)continue;w[a]=b;
T.modify(1,1,cnt,pos[a],a,b);
T.modify(1,1,cnt,pos[fa[a]],a,b);
}
}
return 0;
}