题解 机动车限行
考场上转化为了一些形如「集合 S 中至少选一个」的限制,但是不会做
然而发现每个点一定恰好出现在两个集合中,所以如果把每个连通块看作一个点,这个图就是一个二分图
具体地,以 1 公司为例
先删去所有 2 的i点,并查集缩一次点,这些点形成了 A 部点
再删去所有 3 的i点,并查集缩一次点,这些点形成了 B 部点
如果存在一个 1 点同时出现在了一个 A 部点和一个 B 部点中,就在这两个点间连边
选这条边代表将这个 1 点设为物流中心
于是问题变为了二分图最小边覆盖+输出方案,dinic 即可
输出方案的话有流量的边一定选,没有流量的点可以任选一条出边
复杂度是
其实如果颜色再多几种的话貌似也可以做,一般图最大匹配就好了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
int a[N], head[N], size;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++size]={t, head[s]}; head[s]=size;}
namespace force{
int ans=INF, tem[5];
bool cet[N], vis[N];
vector<int> v[5];
void dfs(int u, int ban) {
vis[u]=1;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (!vis[v] && a[v]!=ban) dfs(v, ban);
}
}
bool check() {
for (int i=1; i<=n; ++i) vis[i]=0;
for (int i=1; i<=n; ++i) if (a[i]!=1 && !vis[i] && cet[i]) dfs(i, 1);
for (int i=1; i<=n; ++i) if (a[i]!=1 && !vis[i]) return 0;
for (int i=1; i<=n; ++i) vis[i]=0;
for (int i=1; i<=n; ++i) if (a[i]!=2 && !vis[i] && cet[i]) dfs(i, 2);
for (int i=1; i<=n; ++i) if (a[i]!=2 && !vis[i]) return 0;
for (int i=1; i<=n; ++i) vis[i]=0;
for (int i=1; i<=n; ++i) if (a[i]!=3 && !vis[i] && cet[i]) dfs(i, 3);
for (int i=1; i<=n; ++i) if (a[i]!=3 && !vis[i]) return 0;
for (int i=1; i<=3; ++i) tem[i]=0;
for (int i=1; i<=n; ++i) if (cet[i]) ++tem[a[i]];
for (int i=1; i<=3; ++i) if (!tem[i]) return 0;
for (int i=1; i<=3; ++i) v[i].clear();
for (int i=1; i<=n; ++i) if (cet[i]) v[a[i]].push_back(i);
return 1;
}
void solve() {
int lim=1<<n;
for (int s=0; s<lim; ++s) {
int cnt=0;
for (int i=1; i<=n; ++i)
if (s&(1<<(i-1))) cet[i]=1, ++cnt;
else cet[i]=0;
if (cnt<ans) {
if (check()) ans=cnt;
}
}
for (int i=1; i<=3; ++i) {
cout<<v[i].size()<<' '; for (auto it:v[i]) cout<<it<<' '; cout<<endl;
}
}
}
namespace task{
int sta[N], top, s, t;
map<pair<int, int>, bool> mp;
struct graph{
int dsu[N], vis[N], sta[N], top;
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
void clear() {top=0; for (int i=1; i<=n; ++i) dsu[i]=i, vis[i]=0;}
void build(int now, int ban) {
for (int i=1; i<=n; ++i) if (a[i]!=ban) {
for (int j=head[i],v; ~j; j=e[j].next) {
v = e[j].to;
if (a[v]!=ban) dsu[find(v)]=find(i);
}
}
for (int i=1; i<=n; ++i) if (a[i]==now) vis[find(i)]=i;
for (int i=1; i<=n; ++i) if (vis[i]) sta[++top]=i;
}
}g1, g2;
namespace dinic{
int head[N], dep[N], cur[N], size;
struct edge{int from, to, next, val, id;}e[N<<1];
inline void add(int s, int t, int w, int id) {e[++size]={s, t, head[s], w, id}; head[s]=size;}
void clear() {size=1; memset(head, -1, sizeof(head));}
bool bfs(int s, int t) {
memset(dep, 0, sizeof(dep));
queue<int> q;
cur[s]=head[s]; dep[s]=1;
q.push(s);
int u;
while (q.size()) {
u=q.front(); q.pop();
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (e[i].val && !dep[v]) {
dep[v]=dep[u]+1;
cur[v]=head[v];
if (v==t) return 1;
q.push(v);
}
}
}
return 0;
}
int dfs(int u, int in) {
if (u==t||!in) return in;
int rest=in, tem;
for (int i=cur[u],v; ~i; cur[u]=i=e[i].next) {
v = e[i].to;
if (e[i].val && dep[v]==dep[u]+1) {
tem=dfs(v, min(rest, e[i].val));
if (!tem) dep[v]=0;
rest-=tem;
e[i].val-=tem;
e[i^1].val+=tem;
if (!rest) break;
}
}
return in-rest;
}
void dinic(int s, int t) {
int ans=0;
while (bfs(s, t)) ans+=dfs(s, INF);
// cout<<"ans: "<<ans<<endl;
for (int i=2; i<=size; i+=2)
if (e[i].id && !e[i].val) sta[++top]=e[i].id;
else if (!e[i].id && e[i].val) {
int t=min(e[i].from, e[i].to);
for (int j=head[t]; ~j; j=e[j].next)
if (e[j].id) {
sta[++top]=e[j].id;
break;
}
}
}
}
void getans(int col, int col1, int col2) {
top=0; s=n*2+1; t=n*2+2;
g1.clear(); g2.clear(); mp.clear();
g1.build(col, col1); g2.build(col, col2);
dinic::clear();
for (int i=1; i<=g1.top; ++i) dinic::add(s, g1.sta[i], 1, 0), dinic::add(g1.sta[i], s, 0, 0);
for (int i=1; i<=g2.top; ++i) dinic::add(g2.sta[i]+n, t, 1, 0), dinic::add(t, g2.sta[i]+n, 0, 0);
for (int i=1; i<=n; ++i) if (a[i]==col) {
if (mp.find({g1.find(i), g2.find(i)+n})==mp.end()) {
dinic::add(g1.find(i), g2.find(i)+n, 1, i), dinic::add(g2.find(i)+n, g1.find(i), 0, i);
mp[{g1.find(i), g2.find(i)+n}]=1;
}
}
dinic::dinic(s, t);
printf("%d ", top);
for (int i=1; i<=top; ++i) printf("%d ", sta[i]);
printf("\n");
}
void solve() {
getans(1, 2, 3);
getans(2, 1, 3);
getans(3, 1, 2);
}
}
signed main()
{
freopen("restriction.in", "r", stdin);
freopen("restriction.out", "w", stdout);
n=read(); m=read();
memset(head, -1, sizeof(head));
for (int i=1; i<=n; ++i) a[i]=read();
for (int i=1,u,v; i<=m; ++i) {
u=read(); v=read();
add(u, v); add(v, u);
}
// force::solve();
task::solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】