LGP9545 [HBTS-Pre 2023] 环山危路 学习笔记

LGP9545 [HBTS-Pre 2023] 环山危路 学习笔记

Luogu Link

题意简述

给定一个 \(n\) 个结点的竞赛图,每条边容量为 \(1\)\(m\) 次询问,每次给定 \(k_i\) 个源点 \(s_{i,j}\)\(1\) 个汇点 \(t_i\)(保证所有 \(s_i\)\(t_i\) 互不相同),求最大流。

\(n\le 3\times 10^3,m\le 3\times 10^4\)

竞赛图的定义:满足任意两点间恰有一条有向边的简单图。

做法解析

直接跑最大流肯定不行,会爆。由于最大流最小割定理,我们求最大流不行就试试考虑最小割,因为这图是张竞赛图,我们可以期待其上的割有一些性质……?

确实有性质:对于两个点集 \(S,T\),我们有 \(f(S,T)+f(T,S)=|S|\times |T|\),因为 \(S,T\) 的点两两有边相连。

另外,我们还有 \(f(S,T)-f(T,S)=\sum_{u\in S} oud_u-ind_u\)。这是因为从 \(S\) 连到 \(T\) 的边会做出 \(1\) 的贡献,从 \(T\) 连到 \(S\) 的边会做出 \(-1\) 的贡献,\(S\) 内部的边做出 \(0\) 的贡献,化一下就可以得到上式。

所以我们就有 \(f(S,T)=\frac{1}{2}(|S|\times |T|+\sum_{u\in S}oud_u-ind_u)\)

这样,对于每个 \(|S|\),我们最小化 \(\sum_{u\in S} oud_u-ind_u\) 并更新答案,就可以得出答案。

实现上就是先算一遍初始 \(S\) 的答案,然后每次贪心地往 \(S\) 里面加 \(oud_u-ind_u\) 最小的点尝试更新答案。

代码实现

真是精彩的签到!

#include <bits/stdc++.h>
using namespace std;
namespace obasic{
    template <typename _T>
    void readi(_T &x){
        _T k=1;x=0;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
        x*=k;return;
    }
    template <typename _T>
    void writi(_T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)writi(x/10);
        putchar(x%10+'0');
    }
    template <typename _T>
    void minner(_T &x,_T y){x=min(x,y);}
};
using namespace obasic;
const int MaxN=3e3+5;
int N,M,T,K,X;char S[MaxN];
int deg[MaxN],vis[MaxN];
struct anob{int v,id;}P[MaxN];
bool cmpv(anob a,anob b){return a.v<b.v;}
int main(){
    readi(N),readi(M);
    for(int i=1;i<=N;i++){
        scanf("%s",S+1);
        for(int j=1;j<=N;j++)if(S[j]=='1')deg[i]++,deg[j]--;
    }
    for(int i=1;i<=N;i++)P[i]={deg[i],i};
    sort(P+1,P+N+1,cmpv);queue<int> q;
    for(int csiz,csum;M--;){
        readi(T),readi(K);csiz=K,csum=0;
        for(int i=1;i<=K;i++)readi(X),vis[X]=1,q.push(X),csum+=deg[X];
        int ans=(csiz*(N-csiz)+csum)/2;
        for(int i=1;i<=N;i++){
            auto [cv,cid]=P[i];
            if(cid==T||vis[cid])continue;
            csiz++,csum+=cv;minner(ans,(csiz*(N-csiz)+csum)/2);
        }
        writi(ans),puts("");
        while(!q.empty())vis[q.front()]=0,q.pop();
    }
    return 0;
}

反思总结

想求 \(x\) 时,如果实在别的方法都不行,试试求 \(x+y\)\(x-y\)
神奇吧,HBTS-Pre。

posted @ 2025-02-26 17:05  矞龙OrinLoong  阅读(8)  评论(0)    收藏  举报