洛谷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 '\n'
#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

 

posted @ 2020-07-13 21:59  Anonytt  阅读(150)  评论(0编辑  收藏  举报