• UOJ #30【CF Round #278】Tourists


    求从$ x$走到$ y$的路径上可能经过的最小点权,带修改  UOJ #30


    $ Solution:$

    如果两个点经过了某个连通分量,一定可以走到这个连通分量的最小值

    直接构建圆方树,圆点存原点的点权,方点用$ multiset$存连通分量的点权集合,权值为集合中的最小值

    每次询问就变成求圆方树中一条树链的最小值,可以用树链剖分维护

    考虑修改一个圆点的点权

    对于这个圆点直接修改,但如果暴力修改周围方点,复杂度明显爆炸

    我们修改一下方点的定义,改为这个连通分量中除了自己父亲圆点外其他圆点的最小权值

    这样修改一个圆点的权值的时候只需要这个圆点的父亲方点的权值

    每次询问的时候如果$ LCA$为方点还要额外考虑这个方点的父亲圆点的贡献

    这样就可以在$ O(n log^2 n)$的时间复杂度内解决这个问题

    $ my code:$

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<set>
    #define M 200010
    #define rt register int
    #define ll long long
    using namespace std;
    inline ll read(){
        ll x = 0; char zf = 1; char ch = getchar();
        while (ch != '-' && !isdigit(ch)) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    int i,j,k,m,n,x,y,z,cnt;
    int F[M],L[M],N[M],a[M],c[M],dfn[M],low[M],w[M],v[M];
    void add(int x,int y){
        a[++k]=y;
        if(!F[x])F[x]=k;
        else N[L[x]]=k;
        L[x]=k;
    }
    int sta[M],top;
    vector<int>e[M];
    multiset<int>s[M];
    void Add(int x,int y){
        e[x].push_back(y);
        e[y].push_back(x);
    } 
    void tarjan(int x){
        dfn[x]=low[x]=++cnt;sta[++top]=x;
        for(rt i=F[x];i;i=N[i]){
            if(!dfn[a[i]]){
                tarjan(a[i]),low[x]=min(low[x],low[a[i]]);
                if(low[a[i]]>=dfn[x]){
                    n++;
                    while(sta[top+1]!=a[i])s[n].insert(w[sta[top]]),Add(sta[top--],n);
                    Add(x,n);
                    if(!s[n].size())w[n]=999999999;else w[n]=*s[n].begin();
                }
            }
            low[x]=min(low[x],dfn[a[i]]);
        }
    }
    char getopt(){
        char c=getchar();
        while(c!='A'&&c!='C')c=getchar();
        return c;
    }
    int size[M],deep[M],fa[M],to[M],up[M],pl[M],NN;
    void dfs(int x,int pre){
        size[x]=1;fa[x]=pre;
        for(auto i:e[x])if(i!=pre)deep[i]=deep[x]+1,dfs(i,x),size[x]+=size[i];
    }
    void dfs2(int x,int chain){
        dfn[x]=++cnt;to[cnt]=x;int heavy=0;up[x]=chain;
        for(auto i:e[x])if(i!=fa[x])if(size[i]>size[heavy])heavy=i;
        if(!heavy)return;
        dfs2(heavy,chain);
        for(auto i:e[x])if(i!=heavy&&i!=fa[x])dfs2(i,i);
    }
    struct seg{
        int L,R,Min;
    }t[M*4];
    void build(int x,int L,int R){
        t[x].L=L;t[x].R=R;
        if(L==R)return t[x].Min=w[to[L]],pl[to[L]]=x,void();
        const int mid=L+R>>1;
        build(x<<1,L,mid);build(x<<1|1,mid+1,R);
        t[x].Min=min(t[x<<1].Min,t[x<<1|1].Min);
    }
    inline int min(const int &x,const int &y){
        return x<y?x:y;
    }
    int query(int x,int L,int R){
        if(t[x].L>=L&&t[x].R<=R)return t[x].Min;
        const int mid=t[x].L+t[x].R>>1;
        if(R<=mid)return query(x<<1,L,R);
        if(L>mid)return query(x<<1|1,L,R);
        return min(query(x<<1,L,R),query(x<<1|1,L,R));
    }
    void change(int x,int val){
        x=pl[x];t[x].Min=val;x>>=1;
        while(x){
            t[x].Min=min(t[x<<1].Min,t[x<<1|1].Min);
            x>>=1;
        }
    }
    int get(int x,int y){
        int Min=999999999;
        while(up[x]!=up[y]){
            if(deep[up[x]]<deep[up[y]])swap(x,y);
            Min=min(Min,query(1,dfn[up[x]],dfn[x]));
            x=fa[up[x]];
        }
        if(deep[x]<deep[y])swap(x,y);
        Min=min(Min,query(1,dfn[y],dfn[x]));
        if(y>NN)Min=min(Min,w[fa[y]]);
        return Min;
    }
    int main(){
        n=NN=read();m=read();int q=read();
        for(rt i=1;i<=n;i++)w[i]=read();
        for(rt i=1;i<=m;i++){
            x=read();y=read();
            add(x,y);
            add(y,x);
        }
        tarjan(1);cnt=0;
        dfs(1,1);dfs2(1,1);build(1,1,n);
        while(q--){
            char c=getopt();x=read();y=read();
            if(c=='C'){            
                if(x!=1){
                    s[fa[x]].erase(s[fa[x]].find(w[x]));
                    s[fa[x]].insert(y);
                    int v=*s[fa[x]].begin();
                    if(v!=w[fa[x]]){
                        w[fa[x]]=v;
                        change(fa[x],v);
                    }
                }
                w[x]=y;
                change(x,y);
                continue;
            }
            writeln(get(x,y));
        }
        return 0;
    }
  • 相关阅读:
    C#编程思路
    将字符串类型字段转为map类型字段,使用str_to_map()函数
    写hive脚本时,如果hive的过滤条件比较多。可以把过滤条件放到一个参数里。然后把参数放到过滤条件处。这样以后只需要改参数就可以了
    linux中. 路径/文件
    inner join ,left join 会导致数据发散
    如何批量按分区插入数据
    hive表添加字段后,查不出数据是咋回事?
    linux中$0的含义
    linux中的$#含义
    linux的语法
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10020262.html
Copyright © 2020-2023  润新知