题解 机动车限行

传送门

考场上转化为了一些形如「集合 S 中至少选一个」的限制,但是不会做
然而发现每个点一定恰好出现在两个集合中,所以如果把每个连通块看作一个点,这个图就是一个二分图
具体地,以 1 公司为例
先删去所有 2 的i点,并查集缩一次点,这些点形成了 A 部点
再删去所有 3 的i点,并查集缩一次点,这些点形成了 B 部点
如果存在一个 1 点同时出现在了一个 A 部点和一个 B 部点中,就在这两个点间连边
选这条边代表将这个 1 点设为物流中心
于是问题变为了二分图最小边覆盖+输出方案,dinic 即可
输出方案的话有流量的边一定选,没有流量的点可以任选一条出边
复杂度是 \(O(n\log n+m\sqrt n)\)
其实如果颜色再多几种的话貌似也可以做,一般图最大匹配就好了

点击查看代码
#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;
}
posted @ 2022-01-21 20:25  Administrator-09  阅读(0)  评论(0编辑  收藏  举报