P2500 [SDOI2012] 集合

题意

给一张无向图,\(n\) 个点,\(m\) 条边,有边权,每个点属于 \(A,B,C\) 三个集合中的一个,初始都在 \(A\) 集合。
\(q\) 次操作,分为两种:

  1. Move A/B/C u 把点 \(u\),从祂原集合中删掉,添加到集合 \(A/B/C\) 中。
  2. Ask AA/BB/CC/AB/AC/BC 问两个端点分别在 \(AA/BB/CC/AB/AB/BC\) 集合中的边的最小权值。

\(n,q\le10^5,m\le5\times 10^5\)

思路

使用根号分治。
把度数大于 \(B\) 的点成为关键点,对每个关键点开 \(3\)\(multiset\),维护祂的临接点在 \(A/B/C\) 集合中的边。对于两端都是非关键点的边,开 \(6\)\(set\) 维护两端分别为 \(AA/BB/CC/AB/AC/BC\) 的边。
询问和修改直接暴力,由于关键点的数量最多是 \(\frac nB\),非关键点的度数最多是 \(B\)\(B\)\(\sqrt n\),时间复杂度 \(\mathcal O(q\sqrt n\log m)\),但由于跑不满,所以跑的飞快。

代码

/*
Luogu P2499 [SDOI2012] 象棋
2026-04-13
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=110,inf=100000,maxk=510;
int n,m,k,a,b,bh[maxn][maxn],cnt_bh,dis[maxn*maxn];
char c[maxn][maxn];
vector<int>fx,fy;
struct Point{int x,y;}be[maxk],en[maxk];
template<int maxn,int maxm>struct LSQXX{
    int head[maxn],nxt[maxm*2],to[maxm*2],val[maxm*2],cost[maxm*2],cnt=1;
    void add(int u,int v,int z,int c){nxt[++cnt]=head[u],to[cnt]=v,val[cnt]=z,cost[cnt]=c,head[u]=cnt;}
    void clear(){memset(head,0,sizeof(head)),cnt=1;}
};
void bfs(int x,int y){
    memset(dis,0x3f,sizeof(dis));
    queue<pair<int,int>>q;
    q.push({x,y});dis[bh[x][y]]=0;
    while(!q.empty()){
        auto[x,y]=q.front();q.pop();
        for(int i=0;i<8;i++){
            int xx=x+fx[i],yy=y+fy[i];
            if(xx<1||xx>n||yy<1||yy>m) continue;
            if(c[xx][yy]=='*') continue;
            if(dis[bh[xx][yy]]>dis[bh[x][y]]+1) dis[bh[xx][yy]]=dis[bh[x][y]]+1,q.push({xx,yy});
        }
    }
}
class Network_Flow{
private:
    LSQXX<maxk*2,maxk*maxk>e;
    int cur[maxk*2],s,t,ans2,dis[maxk*2];
    bool inque[maxk*2],vis[maxk*2];
    void edge_add(int u,int v,int w,int z){e.add(u,v,w,z),e.add(v,u,0,-z);}
    bool spfa(){
        for(int i=s;i<=t;i++) inque[i]=0,dis[i]=inf;
        queue<int>q;q.push(s);dis[s]=0;
        while(!q.empty()){
            int u=q.front();q.pop();inque[u]=0;
            for(int i=e.head[u];i;i=e.nxt[i]){
                int v=e.to[i];
                if(e.val[i]==0) continue;
                if(dis[v]>dis[u]+e.cost[i]){
                    dis[v]=dis[u]+e.cost[i];
                    if(!inque[v]) q.push(v),inque[v]=1;
                }
            }
        }
        return dis[t]!=inf;
    }
    int dfs(int u,int flow=inf){
        if(flow==0||u==t) return flow;
        int ans=0;
        if(vis[u]) return 0;vis[u]=1;
        for(int&i=cur[u];i;i=e.nxt[i]){
            int v=e.to[i];
            if(dis[v]!=dis[u]+e.cost[i]) continue;
            int new_flow=dfs(v,min(flow,e.val[i]));
            flow-=new_flow,ans+=new_flow,ans2+=new_flow*e.cost[i];
            e.val[i]-=new_flow,e.val[i^1]+=new_flow;
            if(flow==0) return vis[u]=0,ans;
        }
        vis[u]=0;
        return ans;
    }
public:
    void build(){
        s=0,t=k*2+1;
        for(int i=1;i<=k;i++){
            edge_add(s,i,1,0);
            bfs(be[i].x,be[i].y);
            for(int j=1;j<=k;j++) edge_add(i,j+k,1,::dis[bh[en[j].x][en[j].y]]);
            edge_add(i+k,t,1,0);
        }
    }
    int work(){
        while(spfa()){
            for(int i=s;i<=t;i++) cur[i]=e.head[i];
            dfs(s);
        }
        return ans2;
    }
}wll;
signed main(){
    read(n,m,k,a,b);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) read(c[i][j]),bh[i][j]=++cnt_bh;
    fx={a,a,b,b,-a,-a,-b,-b};
    fy={b,-b,a,-a,b,-b,a,-a};
    for(int i=1;i<=k;i++) read(be[i].x,be[i].y);
    for(int i=1;i<=k;i++) read(en[i].x,en[i].y);
    wll.build();
    write(wll.work());
    return 0;
}
posted @ 2026-04-16 21:29  Link-Cut_Trees  阅读(6)  评论(0)    收藏  举报