[点双连通分量 缩点 树链剖分] Codeforces 487E #278 (Div. 1) E. Tourists

来源:互联网 发布:java商品信息管理系统 编辑:程序博客网 时间:2024/05/17 08:00

点双连通分量的缩点技巧

来自:http://blog.csdn.net/a_crazy_czy/article/details/52244069


在这里我们使用这样一种方法:对于每一个点双连通分量,我们建一个新建点储存点双所有点的最小权值,然后该点向点双内所有连一条边,然后深度最小的点(大多情况为割点,当然树根不一定是割点)向这个新建点连边。 
注意到这棵树一定是普通点连向新建点连向普通点这样交替。修改的时候我们修改这个点自身的权值,还要把这个点所属的新建点(父亲节点)修改了。查询的时候呢?首先先查询两点之间路径的最小值,然后如果两点LCA是新建点,那就还要查询它的父亲的最小值更新。 
为什么这样是对的呢?因为查询的时候,除了最顶端LCA所在的点双,其它点的点双的顶点都是肯定能够经过的,查询是不会出错的,最顶端的LCA如果是一个新建点,那么就证明我还是可以通过该点双的顶点的,但是我没有用这个顶点更新过点双,因此要再用这个顶点(即LCA父亲)更新一下答案。否则这是一个顶点,如果我要经过该点双其它位置,那么必然要经过顶点两次,那是不行的,因此不用管。 
关于怎么维护点双内最小值,对每一个新建点开一个multiset就好了。 

大概是这样 灵魂画作




#include<cstdio>#include<cstdlib>#include<algorithm>#include<stack>#include<set>#include<cstring>#define cl(x) memset(x,0,sizeof(x))using namespace std;inline char nc(){  static char buf[100000],*p1=buf,*p2=buf;  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }  return *p1++;}inline void read(int &x){  char c=nc(),b=1;  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;}inline void read(char &x){  for (x=nc();x!='A' && x!='C';x=nc());}const int N=200005;struct SEG{  int T[N<<2],M;  inline void Build(int n){    for (M=1;M<n+2;M<<=1);    for (int i=2*M-1;i;i--)      T[i]=1<<30;  }  inline void Modify(int s,int r){    T[s+=M]=r;    while (s>>=1)      T[s]=min(T[s<<1],T[s<<1|1]);  }  inline int Query(int s,int t){    int ret=1<<30;    for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){      if (~s&1) ret=min(ret,T[s^1]);      if ( t&1) ret=min(ret,T[t^1]);    }    return ret;  }}Seg;struct edge{  int u,v,next;}G[N<<4];int head1[N],head2[N],inum=1;int *head;inline void add(int u,int v,int p,int *head=::head){  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;}stack<int> S;multiset<int> Set[100005];int pre[N],low[N],clk;#define V G[p].vint bcc;int n,m,val[N];inline void Tarjan(int u,int fa){  pre[u]=low[u]=++clk; S.push(u);  for (int p=head[u];p;p=G[p].next)    if (!pre[V]){      Tarjan(V,p);      low[u]=min(low[u],low[V]);      if (low[V]>=pre[u]){bcc++;add(u,n+bcc,++inum,head2),add(n+bcc,u,++inum,head2);while (1){  int x=S.top(); S.pop();   add(n+bcc,x,++inum,head2),add(x,n+bcc,++inum,head2);  Set[bcc].insert(val[x]);  if (x==V) break;}      }    }else if (p!=(fa^1))      low[u]=min(low[u],pre[V]);}int depth[N],size[N],fat[N];int tid[N],top[N];inline void dfs(int u,int fa){  depth[u]=depth[fa]+1; size[u]=1; fat[u]=fa;  for (int p=head[u];p;p=G[p].next)    if (V!=fa)      dfs(V,u),size[u]+=size[V];}inline void find(int u,int fa,int z){  tid[u]=++clk; top[u]=z;  int son=0,maxv=0;  for (int p=head[u];p;p=G[p].next)    if (V!=fa && size[V]>maxv)      maxv=size[son=V];  if (son) find(son,u,z);  for (int p=head[u];p;p=G[p].next)    if (V!=fa && V!=son)      find(V,u,V);}inline int Query(int u,int v){  int ret=1<<30;  for (;top[u]!=top[v];u=fat[top[u]]){    if (depth[top[u]]<depth[top[v]]) swap(u,v);    ret=min(ret,Seg.Query(tid[top[u]],tid[u]));  }  if (depth[u]>depth[v]) swap(u,v);  ret=min(ret,Seg.Query(tid[u],tid[v]));  int lca=u;  if (lca>n)    ret=min(ret,val[fat[lca]]);  return ret;}int main(){  int Q,iu,iv; char order;  freopen("t.in","r",stdin);  freopen("t.out","w",stdout);  read(n); read(m); read(Q);  for (int i=1;i<=n;i++) read(val[i]);  head=head1;  for (int i=1;i<=m;i++)    read(iu),read(iv),add(iu,iv,++inum),add(iv,iu,++inum);  Tarjan(1,0);  head=head2;  clk=0; dfs(1,0); find(1,0,1);  for (int i=1;i<=bcc;i++) val[n+i]=*Set[i].begin();  Seg.Build(n+bcc);  for (int i=1;i<=n+bcc;i++) Seg.Modify(tid[i],val[i]);  while (Q--){    read(order); read(iu); read(iv);    if (order=='A')      printf("%d\n",Query(iu,iv));    else{      if (fat[iu]){int fa=fat[iu];Set[fa-n].erase(Set[fa-n].find(val[iu]));Set[fa-n].insert(iv);val[fa]=*Set[fa-n].begin();Seg.Modify(tid[fa],val[fa]);      }      val[iu]=iv;       Seg.Modify(tid[iu],val[iu]);    }  }  return 0;}



0 0
原创粉丝点击