[uoj30][CF Round #278]Tourists——树链剖分+圆方树 大佬们的博客 Some Links

题目大意:

给定一个无向图,操作一可以修改一个点的点权,操作二询问从x到y的点不重复路径中的点权最小值。

思路:

无向图询问点不重复路径中的点权最小值,路径的条数很多,但是我们发现如果路上有两点在一个点双内,那么这两点之间的路径是可以经过这个点双内的任意一点而获得这个点双之内的最小值的,所以只需要对原图建出圆方树就好了。
建出圆方树之后圆点为原来的权值,方点为它周围所有圆点的权值的最小值,也就是它代表的点双的最小值。然后用树链剖分去维护。
但是发现一个问题,就是一个割点可能属于很多个点双,如果修改了一个割点的权值,那么相应的,它周围所有的方点的权值都要更新,这样显然复杂度就不对了。
又是一个小技巧,把圆方树固定了根结点之后,每一个方点只维护它所有儿子的权值,也就是每一个圆点的权值只在它父亲方点那里维护。如果在跳链的时候跳到了这个圆点的儿子方点,那么要么就是继续往上跳,否则这里就是公共祖先,如果继续往上跳的话就刚好把这个圆点算在了内部,如果这是公共祖先的话就在询问的时候特判它父亲的圆点的权值。
所以每一个圆点的修改只需要更新一个方点了,复杂度就很正了。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("uoj30.in","r",stdin);
    freopen("uoj30.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
int n,m,q,w[maxn<<1],tot;
int beg[maxn],to[maxn<<1],las[maxn<<1],cnte=1;
vector<int>G[maxn<<1];
int dfn[maxn],low[maxn],cnt_dfn;
stack<int>stk;
multiset<int>st[maxn<<1];
multiset<int>::iterator it;

void add(int u,int v){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u;
}

void tarjan(int u,int f){
    stk.push(u);
    low[u]=dfn[u]=++cnt_dfn;
    for(int i=beg[u];i;i=las[i]){
        if(to[i]==f)continue;
        if(!dfn[to[i]]){
            tarjan(to[i],u);
            low[u]=min(low[u],low[to[i]]);
            if(low[to[i]]>=dfn[u]){
                ++tot;
                for(int p;p!=to[i];stk.pop()){
                    p=stk.top();
                    G[tot].push_back(p);
                    G[p].push_back(tot);
                }
                G[tot].push_back(u);
                G[u].push_back(tot);
            }
        }
        else low[u]=min(low[u],dfn[to[i]]);
    }
}

void dfs(int u,int f){
    for(int sz=G[u].size()-1,i=0;i<=sz;++i){
        if(G[u][i]==f)continue;
        //cout<<u<<" "<<G[u][i]<<endl;
        if(u>n)st[u].insert(w[G[u][i]]);
        dfs(G[u][i],u);
    }
    if(u>n)it=st[u].begin(),w[u]=*it;
    //cout<<u<<" "<<w[u]<<endl;
}

struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc rt<<1
#define rc rt<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
    int Min[maxn<<3];
    void update(int rt,int l,int r,int pos,int x){
        if(l==r){Min[rt]=x; return;}
        if(pos<=mid)update(lson,pos,x);
        else update(rson,pos,x);
        Min[rt]=min(Min[lc],Min[rc]);
    }
    int query(int rt,int l,int r,int L,int R){
        if(L<=l && r<=R)return Min[rt];
        int ret=inf;
        if(L<=mid)ret=query(lson,L,R);
        if(R>=mid+1)ret=min(ret,query(rson,L,R));
        return ret;
    }
}T;

int dep[maxn<<1],fa[maxn<<1],son[maxn<<1],siz[maxn<<1],top[maxn<<1],in[maxn<<1],cnt_tree;

void dfs1(int u,int f){
    dep[u]=dep[f]+1; siz[u]=1; fa[u]=f;
    for(int sz=G[u].size()-1,i=0;i<=sz;++i){
        if(G[u][i]==f)continue;
        dfs1(G[u][i],u);
        siz[u]+=siz[G[u][i]];
        if(siz[G[u][i]]>siz[son[u]])
            son[u]=G[u][i];
    }
}

void dfs2(int u,int tp){
    in[u]=++cnt_tree; top[u]=tp;
    T.update(1,1,tot,in[u],w[u]);
    if(son[u])dfs2(son[u],tp);
    for(int sz=G[u].size()-1,i=0;i<=sz;++i){
        if(G[u][i]==fa[u] || G[u][i]==son[u])continue;
        dfs2(G[u][i],G[u][i]);
    }
}

void modify(int u,int x){
    T.update(1,1,tot,in[u],x);
    if(fa[u]){
        st[fa[u]].insert(x);
        it=st[fa[u]].find(w[u]);
        st[fa[u]].erase(it);
        it=st[fa[u]].begin();
        w[fa[u]]=*it; T.update(1,1,tot,in[fa[u]],w[fa[u]]);
    }
    w[u]=x;
}

int solve(int u,int v){
    int Min=inf;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        Min=min(Min,T.query(1,1,tot,in[top[u]],in[u]));
        u=fa[top[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    Min=min(Min,T.query(1,1,tot,in[v],in[u]));
    if(fa[v] && fa[v]<=n)Min=min(Min,w[fa[v]]);
    return Min;
}

void init(){
    read(n); read(m); read(q);
    REP(i,1,n)read(w[i]);
    int u,v;
    REP(i,1,m)read(u),read(v),add(u,v);
    tot=n; tarjan(1,0); dfs(1,0); 
    memset(T.Min,63,sizeof(T.Min));
    dfs1(1,0); dfs2(1,1);
}

void work(){
    char s[5];
    int u,v;
    REP(i,1,q){
        scanf("%s",s);
        read(u); read(v);
        if(s[0]=='A')printf("%d\n",solve(u,v));
        else modify(u,v);
    }
}

int main(){
    File();
    init();
    work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81734920
今日推荐