cf 487E/uoj 30 Tourists

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值