BZOJ-4777 Switch Grass(最小生成树+动态开点线段树+可删堆)

版权声明:我这种蒟蒻的文章,真正的大佬一般看不上的_(:з」∠)_ https://blog.csdn.net/Paulliant/article/details/83409452

题意

给定一张 n n 和节点, m m 条边的无向图,每个点有一个初始颜色,接下来有 Q Q 个操作,每次操作会更改一个点的颜色,并询问距离最近的不同色点对的最小距离,颜色上限不超过 K K 且图中至少有两种颜色。
1 n , Q 2 × 1 0 5 1 \leq n,Q \leq 2 \times 10^5
1 m 4 × 1 0 5 1 \leq m \leq 4 \times 10^5
1 K 1 0 6 1 \leq K \leq 10^6

思路

首先答案肯定是连接两个不同色点对的最短边,而这个最短边肯定在最小生成树上。前者显然成立,后者可以在 Kruskal \text{Kruskal} 的过程中看出来。
那么首先把图缩成一棵最小生成树并把它拎成一棵有根树。对于每一个节点,我们维护它不同颜色子节点的信息,由此达到不重复。
每次更新,对于一个节点 u u ,它的颜色可能随时会变,设之为 c u c_u ,每次的查询就是连接颜色在区间 [ 1 , c u 1 ] [ c u + 1 , K ] [1,c_u-1]\bigcup[c_u+1,K] 中儿子的边的最小权值,我们不妨把边权拉到子节点上方便处理。而同种颜色的子节点权值不一定相同。颜色权值两维,不妨两个数据结构套用:颜色要查询区间,用线段树维护,直接维护又会炸内存,改用动态开点;而权值要维护住很多值,还要支持删除、插入、查询最小值, multiset \text{multiset} 可以实现,但是可删堆常数更小。
为了快速回答询问,我们实时维护每个节点和它子节点之间的答案,用 multiset/ \text{multiset/} 可删堆 维护这个数组每次查询最小值即可。
用数据结构解题首先要形成清晰的算法流程,再套用合适的数据结构,当然模板要打熟,功底要深厚。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=2e5+5;
const int M=4e5+5;
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],nxt[maxm],cost[maxm],tot;
    Linked_list(){clear();}
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v,int w){to[++tot]=v,cost[tot]=w,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct DisjointSet
{
    int fa[N],n;
    void init(int _n){n=_n;FOR(i,1,n)fa[i]=i;}
    int getfa(int k){return k==fa[k]?k:fa[k]=getfa(fa[k]);}
    bool merge(int x,int y)
    {
        x=getfa(x),y=getfa(y);
        if(x==y)return false;
        fa[x]=y;
        return true;
    }
}DSU;
struct ErasableHeap
{
    priority_queue<int,vector<int>,greater<int> >Q,E;
    void push(int x){Q.push(x);}
    void erase(int x){E.push(x);}
    void modify(){while(!Q.empty()&&!E.empty()&&Q.top()==E.top())Q.pop(),E.pop();}
    void pop(){modify();if(!Q.empty())Q.pop();}
    int top(){modify();if(Q.empty())return 2e9;return Q.top();}
};
struct edge
{
    int u,v,w;
    bool operator <(const edge &_)const{return w<_.w;}
}E[M];
Linked_list<N,N<<1>G;
ErasableHeap ans;
struct node{int lson,rson,miner,id;};
struct SegmentTree
{
    ErasableHeap st[N<<2];
    node nd[N*40];
    int rt[N<<1],tot,ID;
    void build(){memset(rt,0,sizeof(rt));tot=ID=0;nd[0].miner=2e9;}
    void create(int &k){if(k==0)nd[k=++tot]=(node){0,0,2e9,0};}
    void add_up(int k){nd[k].miner=min(nd[nd[k].lson].miner,nd[nd[k].rson].miner);}
    void update(int &k,int x,int A,bool B,int l,int r)
    {
        create(k);
        if(l==r)
        {
            if(nd[k].id==0)nd[k].id=++ID;
            if(B)st[nd[k].id].push(A);
            else st[nd[k].id].erase(A);
            nd[k].miner=st[nd[k].id].top();
            return;
        }
        int mid=l+r>>1;
        if(x<=mid)update(nd[k].lson,x,A,B,l,mid);
        else update(nd[k].rson,x,A,B,mid+1,r);
        add_up(k);
    }
    int query(int &k,int L,int R,int l,int r)
    {
        if(!k)return 2e9;
        if(L<=l&&r<=R)return nd[k].miner;
        int mid=l+r>>1;
        if(R<=mid)return query(nd[k].lson,L,R,l,mid);
        else if(L>mid)return query(nd[k].rson,L,R,mid+1,r);
        else return min(query(nd[k].lson,L,R,l,mid),query(nd[k].rson,L,R,mid+1,r));
    }
}ST;
int fa[N],p[N],minson[N],c[N],n,m,K,Q;
 
void Kruskal()
{
    DSU.init(n);
    sort(E+1,E+1+m);
    FOR(i,1,m)if(DSU.merge(E[i].u,E[i].v))
    {
        G.add(E[i].u,E[i].v,E[i].w);
        G.add(E[i].v,E[i].u,E[i].w);
    }
}
int getMin(int u)
{
    if(c[u]==1)return ST.query(ST.rt[u],c[u]+1,K,1,K);
    else if(c[u]==K)return ST.query(ST.rt[u],1,c[u]-1,1,K);
    else return min(ST.query(ST.rt[u],1,c[u]-1,1,K),ST.query(ST.rt[u],c[u]+1,K,1,K));
}
void dfs(int u,int f)
{
    fa[u]=f;
    EOR(i,G,u)
    {
        int v=G.to[i],w=G.cost[i];
        if(v==f)continue;
        p[v]=w;
        ST.update(ST.rt[u],c[v],p[v],1,1,K);
        dfs(v,u);
    }
    minson[u]=getMin(u);
    ans.push(minson[u]);
}
 
int main()
{
    ST.build();
    scanf("%d%d%d%d",&n,&m,&K,&Q);
    FOR(i,1,m)scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
    FOR(i,1,n)scanf("%d",&c[i]);
    Kruskal();
    dfs(1,0);
    while(Q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(fa[x])
        {
            ST.update(ST.rt[fa[x]],c[x],p[x],0,1,K);
            c[x]=y;
            ST.update(ST.rt[fa[x]],c[x],p[x],1,1,K);
            ans.erase(minson[fa[x]]);
            minson[fa[x]]=getMin(fa[x]);
            ans.push(minson[fa[x]]);
        }
        else c[x]=y;
        ans.erase(minson[x]);
        minson[x]=getMin(x);
        ans.push(minson[x]);
        printf("%d\n",ans.top());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/83409452