[Codeforces 487E]Tourists/[JZOJ4691]旅行/[UOJ#30]Tourists

来源:互联网 发布:替代 usleep windows 编辑:程序博客网 时间:2024/06/11 04:05

题目大意

给定一个有n个点m条边的无向图,每个点有点权wi
q个操作,每次询问所有点x到点y的简单路径上最小点权是多少或者将点x权值改为y
1n,m,q1×105,1wi109


题目分析

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


代码实现

#include <algorithm>#include <iostream>#include <climits>#include <cstring>#include <cstdio>#include <cctype>#include <cmath>#include <stack>#include <set>using namespace std;typedef multiset<int>::iterator ptr;const int INF=INT_MAX;const int N=100050;const int V=N<<1;const int M=N<<1;const int E=M<<1;const int EL=V<<1;const int LGEL=19;multiset<int> bst[V];struct G{    int next[E],tov[E];    int last[V];    int tot;    void insert(int x,int y){tov[++tot]=y,next[tot]=last[x],last[x]=tot;}}g,t;int size[V],hea[V],prt[V],fa[V],DFN[V],LOW[N],val[V],high[V],pos[V];int n,m,q,all,idx,el,lgel;int rmq[EL][LGEL];int euler[EL];stack<int> S;struct segment_tree{    int v[V<<2];    void update(int x){v[x]=min(v[x<<1],v[x<<1|1]);}    void modify(int x,int y,int l,int r,int edit)    {        if (l==r)        {            v[x]=edit;            return;        }        int mid=l+r>>1;        if (y<=mid) modify(x<<1,y,l,mid,edit);        else modify(x<<1|1,y,mid+1,r,edit);        update(x);    }    int query(int x,int st,int en,int l,int r)    {        if (st==l&&en==r) return v[x];        int mid=l+r>>1;        if (en<=mid) return query(x<<1,st,en,l,mid);        else if (mid+1<=st) return query(x<<1|1,st,en,mid+1,r);        else return min(query(x<<1,st,mid,l,mid),query(x<<1|1,mid+1,en,mid+1,r));    }}T;void tarjan(int x,int fr){    DFN[x]=LOW[x]=++idx,S.push(x);    for (int i=g.last[x],y;i;i=g.next[i])        if (!DFN[y=g.tov[i]])        {            tarjan(y,x);            LOW[x]=min(LOW[x],LOW[y]);            if (LOW[y]>=DFN[x])            {                int sp=++all;                t.insert(x,sp);                for (int u;;)                {                    u=S.top(),S.pop(),t.insert(sp,u),bst[sp].insert(val[u]);                    if (u==y) break;                }            }        }        else if (y!=fr) LOW[x]=min(LOW[x],DFN[y]);}void dfs(int x){    size[x]=1,hea[x]=0,rmq[pos[euler[++el]=x]=el][0]=x;    for (int i=t.last[x],y;i;i=t.next[i])    {        high[y=t.tov[i]]=high[x]+1,fa[y]=x,dfs(y),euler[++el]=x,rmq[el][0]=x,size[x]+=size[y];        if (!hea[x]||size[hea[x]]<size[y]) hea[x]=y;    }}void build(int x,int fr){    DFN[x]=++idx,prt[x]=fr;    if (!hea[x]) return;    build(hea[x],fr);    for (int i=t.last[x],y;i;i=t.next[i])        if ((y=t.tov[i])!=hea[x]) build(y,y);}void pre(){    lgel=trunc(log(el)/log(2));    for (int j=1;j<=lgel;j++)        for (int i=1;i+(1<<j)-1<=el;i++)            rmq[i][j]=high[rmq[i][j-1]]<high[rmq[i+(1<<j-1)][j-1]]?rmq[i][j-1]:rmq[i+(1<<j-1)][j-1];    for (int i=n+1;i<=all;i++) val[i]=*bst[i].begin();    for (int i=1;i<=all;i++) T.modify(1,DFN[i],1,idx,val[i]);}int getrmq(int l,int r){    int lgr=trunc(log(r-l+1)/log(2));    return high[rmq[l][lgr]]<high[rmq[r-(1<<lgr)+1][lgr]]?rmq[l][lgr]:rmq[r-(1<<lgr)+1][lgr];}int lca(int x,int y){    if ((x=pos[x])>(y=pos[y])) swap(x,y);    return getrmq(x,y);}int getans(int x,int y){    int z=lca(x,y),ret=INF;    for (int u=prt[x];prt[x]!=prt[z];x=fa[u],u=prt[x]) ret=min(ret,T.query(1,DFN[u],DFN[x],1,idx));    ret=min(ret,T.query(1,DFN[z],DFN[x],1,idx));    for (int u=prt[y];prt[y]!=prt[z];y=fa[u],u=prt[y]) ret=min(ret,T.query(1,DFN[u],DFN[y],1,idx));    ret=min(ret,T.query(1,DFN[z],DFN[y],1,idx));    if (z>n) ret=min(ret,val[fa[z]]);    return ret;}int main(){    freopen("tourists.in","r",stdin),freopen("tourists.out","w",stdout);    scanf("%d%d%d",&n,&m,&q);    for (int i=1;i<=n;i++) scanf("%d",&val[i]);    for (int i=1,u,v;i<=m;i++)    {        scanf("%d%d",&u,&v);        g.insert(u,v),g.insert(v,u);    }    all=n,tarjan(1,0),high[1]=1,fa[1]=0,dfs(1),idx=0,memset(DFN,0,sizeof DFN),build(1,1),pre();    for (int i=1,a,b;i<=q;i++)    {        char opt=getchar();        while (!isalpha(opt)) opt=getchar();        scanf("%d%d",&a,&b);        if (opt=='C')        {            if (fa[a]) bst[fa[a]].erase(bst[fa[a]].find(val[a]));            val[a]=b,T.modify(1,DFN[a],1,idx,val[a]);            if (fa[a]) bst[fa[a]].insert(val[a]),val[fa[a]]=*bst[fa[a]].begin(),T.modify(1,DFN[fa[a]],1,idx,val[fa[a]]);        }        else printf("%d\n",getans(a,b));    }    fclose(stdin),fclose(stdout);    return 0;}
0 0
原创粉丝点击