• 洛谷P2542 [AHOI2005] 航线规划(树链剖分+离线树转图+思维)


    感谢博主:https://www.luogu.com.cn/blog/116113X/solution-p2542

    题意:略:

    错误解法:这个题我一开始想的是持续动态更新Tarjan双联通缩点后找桥,但是显然从理论上来说复杂度过不去,所以考虑更换思路

    正确解法:这个时候我去搜了一下题解,感觉这个题很不错。因为是双向建边,所以可以用树链剖分来做(LCT没学过不会qwq)。我们先用并查集来保证成树(树上所有的边显然都是关键边),我们把关键边的边权设置为1,这个时候还有一些边是多余的(日后也不会删除),但是我们知道一旦一棵树已经是树了(诶,好绕口哈哈),如果再在树上任意2个点直接添加一条新的边,那么这2个点势必会成为环(换句话说就是这2个点之间就会没有关键边)。那么OK,我们继续想,考虑怎么把多余且日后不会被删的边添加进去。因为此时建成的树是全部都是关键边无环的,所以更新多余边的时候我们就用树链剖分来搞,那些多余的边边权设置为0,把这2个点之间的边权值全部覆盖成0,这时候修改。那么一棵有新的边权的树就更新建成啦。

    接下来就是考虑删边操作。但是正向删边不行,因为删掉一条边又保证联通,显然只能删环上的边,即每次删边后关键边数量一定不减,(假设删边(u,v))若我们每次在u到v的路径上加1,显然无法最优,因为对于u到v路径上的边,不一定删掉(u,v)这条边就一定会让它变成关键边,有可能它还属于别的环。所以考虑用反向操作。我们知道一开始更新完的图就是等同于全部删边删完后的情况,所以我们每次选择反向更新,一开始所有要删掉的边都先不加,相当于默认全部删完了,倒序查询询问后再一条一条加回去,这样就可以避免删边操作了。。把正向删边转换成反向添边。添边操作和上段一样就是把这2个点之前的边权值覆盖为0就ok。然后查询的时候同理用qchain查询就行。

    细节:注意啦。树链剖分维护的是点权值,我们这道题要处理的是边权值,所以树剖板子得改一下(99%不用改),就是qchain和mchain最后那一行dfn[x]需要+1。因为比如我们求x-y之间的边权和,边的数量其实是x-y之间点的数量(包括x和y)再减去1。我们求和或者更新的时候,可以不把dfn[x]考虑在内,就是区间范围去掉dfn[x]这样。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,n) for(int i=a;i<=n;i++)
    #define per(i,n,a) for(int i=n;i>=a;i--)
    #define endl '
    '
    #define eps 0.000000001
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    #define IO ios::sync_with_stdio(false);cin.tie(0);
    using namespace std;
    const int INF=0x3f3f3f3f;
    const ll inf=0x3f3f3f3f3f3f3f3f;
    const int mod=1e9+7;
    const int maxn=1e5+5;
    int tot,head[maxn];
    struct E{
        int to,next;
    }edge[maxn<<1];
    void add(int u,int v){
        edge[tot].to=v;
        edge[tot].next=head[u];
        head[u]=tot++;
    }
    struct nod{
        int t,a,b;
    }dt[maxn];
    int n,m,fa[maxn];
    int find(int x){
        while(x!=fa[x]) x=fa[x]=fa[fa[x]];
        return x;
    }
    int uu[maxn],vv[maxn],vis[maxn];
    map<pair<int,int>,int>mp;
    int v[maxn],root;
    int dep[maxn],siz[maxn],son[maxn];
    void dfs1(int u,int f){
        fa[u]=f;
        dep[u]=dep[f]+1;
        siz[u]=1;
        int maxsize=-1;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(v==f) continue;
            dfs1(v,u);
            siz[u]+=siz[v];
            if(siz[v]>maxsize){
                maxsize=siz[v];
                son[u]=v;
            }
        }
    }
    int tim,dfn[maxn],top[maxn],w[maxn];
    void dfs2(int u,int t){
        dfn[u]=++tim;
        top[u]=t;
        if(!son[u]) return ;
        dfs2(son[u],t);
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(v==fa[u]||v==son[u]) continue;
            dfs2(v,v);
        }
    }
    struct node{
        int l,r,sum,lz;
    }tree[maxn<<2];
    inline void build(int i,int l,int r){
        tree[i].l=l;tree[i].r=r,tree[i].lz=-1;
        if(l==r){
            tree[i].sum=1;
            return ;
        }
        ll mid=(l+r)>>1;
        build(2*i,l,mid);
        build(2*i+1,mid+1,r);
        tree[i].sum=tree[2*i].sum+tree[i*2+1].sum;
    }
    inline void push_down(int i){
        if(tree[i].lz!=-1){
            tree[i*2].lz=tree[i].lz;
            tree[i*2+1].lz=tree[i].lz;
            ll mid=(tree[i].l+tree[i].r)/2;
            tree[i*2].sum=tree[i].lz*(mid-tree[i*2].l+1);
            tree[i*2+1].sum=tree[i].lz*(tree[i*2+1].r-mid);
            tree[i].lz=-1;
        }
        return ;
    }
    inline void modify(int i,int l,int r,int k){
        if(tree[i].r<=r && tree[i].l>=l){
            tree[i].sum=k*(tree[i].r-tree[i].l+1);
            tree[i].lz=k;
            return ;
        }
        push_down(i);
        if(tree[i*2].r>=l)
            modify(i*2,l,r,k);
        if(tree[i*2+1].l<=r)
            modify(i*2+1,l,r,k);
        tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    }
    inline int query(int i,int l,int r){
        if(tree[i].l>=l && tree[i].r<=r)  
            return tree[i].sum;
        if(tree[i].r<l || tree[i].l>r)  return 0;
        push_down(i);
        ll s=0;
        if(tree[i*2].r>=l)  s+=query(i*2,l,r);
        if(tree[i*2+1].l<=r)  s+=query(i*2+1,l,r);
        return s;
    }
    void mchain(int x,int y,int z){
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])
                std::swap(x,y);
            modify(1,dfn[top[x]],dfn[x],z);
            x=fa[top[x]];
        }
        if(dep[x]>dep[y]) std::swap(x,y);
        modify(1,dfn[x]+1,dfn[y],z);
    }
    int qchain(int x,int y){
        int ret=0;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
            ret+=query(1,dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        }
        if(dep[x]>dep[y]) std::swap(x,y);
        ret+=query(1,dfn[x]+1,dfn[y]);
        return ret;
    }
    int main(){
        scanf("%d%d",&n,&m);mem(head,-1);
        rep(i,1,m){
            scanf("%d%d",&uu[i],&vv[i]);
        }
        int cnt=0;
        while(1){
            int op;scanf("%d",&op);
            if(op==-1) break;
            int a,b;scanf("%d%d",&a,&b);
            dt[++cnt]={op,a,b};
            if(!op) mp[{a,b}]=mp[{b,a}]=1;
        }
        rep(i,1,n) fa[i]=i;
        rep(i,1,m){
            int u=find(uu[i]),v=find(vv[i]);
            if(!mp[{uu[i],vv[i]}]&&fa[u]!=v){
                fa[u]=v;
                add(uu[i],vv[i]);
                add(vv[i],uu[i]);
                vis[i]=true;
            }
        }
        mem(fa,0);
        dfs1(1,-1);
        dfs2(1,1);
        build(1,1,n);
        rep(i,1,m){
            if(!vis[i]&&!mp[{uu[i],vv[i]}]){
                int a=uu[i],b=vv[i];
                mchain(a,b,0);
            }
        }
        stack<int> q;
        for(int i=cnt;i>=1;i--){
            int ans=0,t=dt[i].t,a=dt[i].a,b=dt[i].b;
            if(t){
                ans=qchain(a,b);
                q.push(ans);
            }else{
                mchain(a,b,0);
            }
        }
        while(!q.empty()){
            int now=q.top();
            cout<<now<<endl;
            q.pop();
        }
    }
    View Code
  • 相关阅读:
    SpringBoot集成Shiro 实现动态加载权限
    docker 常用命令 以及常见问题
    sql小知识点
    下载文件时-修改文件名字
    关于.net导出数据到excel/word【占位符替换】
    常见js报错
    .net core api +swagger(一个简单的入门demo 使用codefirst+mysql)
    .net core +codefirst(.net core 基础入门,适合这方面的小白阅读,本文使用mysql或mssql)
    基础测试jmeter5.0+badboy(从小白到入门)
    关于ef+codefirst+mysql/dapper(dbFirse)(入门)
  • 原文地址:https://www.cnblogs.com/Anonytt/p/13296146.html
Copyright © 2020-2023  润新知