题解 我

传送门

发现可以对字符建出一张有向图
那么有一些点上有个棋子,每次可以选一条边 \((u, v)\),若 \(u\) 上有棋子则移动到 \(v\)
当两个棋子移动到同一个点上时会合并
求将每条边操作至少一次后最多剩下多少棋子

首先发现若一个强连通分量中含有空点,那么这个强连通分量中的所有边都可以无代价操作
(这个空点可以移动到任意位置)
于是原图可以缩成 DAG
然后发现只要某一时刻一个点空了其所有出边可以在此刻操作一次
所以等价于这个点一定有 1 的外向流量
于是赛时陷入了费用流没有对拍调不出来的困境
其实是不用费用流的
发现一个满的点一定有 1 的外向流量
一个点可以有其中空点个数的容量
一个满点可以以 1 的代价使其变为容量为 1 的空点
那么假设所有有出度的满点都产生了 1 的代价
跑最大流看有多少点不用产生这个代价即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

int n, m;
int top;
char str[N], tr[5];
pair<char, char> g[N];

namespace task1{
	bool vis[150];
	int dis[150], inc[150], back[150];
	int head[150], ecnt, s, t, tot, sum;
	struct edge{int to, next, flw, cst;}e[N<<1];
	inline void add(int s, int t, int f, int c) {/* cout<<"add: "<<s<<' '<<t<<' '<<f<<' '<<c<<endl; */ e[++ecnt]={t, head[s], f, c}; head[s]=ecnt;}
	namespace graph{
		bool exist[150], anynull[150], ins[150];
		int head[150], dfn[150], low[150], siz[150], bel[150], sta[150], top, ecnt, now, tot;
		vector<pair<int, int>> esta;
		map<pair<int, int>, bool> mp;
		struct edge{int to, next;}e[N<<1];
		inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}
		inline int id(char c) {
			if (islower(c)) return c-'a'+1;
			else if (isupper(c)) return c-'A'+27;
			else return c-'0'+53;
		}
		void tarjan(int u) {
			dfn[u]=low[u]=++tot;
			ins[sta[++top]=u]=1;
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (!dfn[v]) {
					tarjan(v);
					low[u]=min(low[u], low[v]);
				}
				else if (ins[v]) low[u]=min(low[u], dfn[v]);
			}
			if (dfn[u]==low[u]) {
				++now;
				do {
					++siz[now];
					ins[sta[top]]=0;
					if (!exist[sta[top]]) anynull[now]=1;
					bel[sta[top--]]=now;
				} while (sta[top+1]!=u);
			}
		}
		void tran() {
			ecnt=now=tot=top=sum=0;
			mp.clear();
			memset(head, -1, sizeof(head));
			memset(exist, 0, sizeof(exist));
			memset(dfn, 0, sizeof(dfn));
			memset(low, 0, sizeof(low));
			memset(ins, 0, sizeof(ins));
			memset(siz, 0, sizeof(siz));
			memset(anynull, 0, sizeof(anynull));
			memset(bel, 0, sizeof(bel));
			esta.clear();
			for (int i=1; i<=n; ++i) exist[id(str[i])]=1;
			for (int i=1; i<=62; ++i) if (exist[i]) ++sum;
			// cout<<"sum: "<<sum<<endl;
			for (int i=1; i<=m; ++i) {
				int u=id(g[i].fir), v=id(g[i].sec);
				if (u==v) continue;
				add(u, v); esta.pb({u, v});
			}
			for (int i=1; i<=62; ++i) if (!dfn[i]) tarjan(i);
			for (auto it:esta) {
				if (bel[it.fir]==bel[it.sec] || mp.find({bel[it.fir], bel[it.sec]})!=mp.end()) continue;
				int u=bel[it.fir], v=bel[it.sec];
				task1::add(u<<1, v<<1, INF, 0), task1::add(v<<1, u<<1, 0, 0);
				task1::add(u<<1, v<<1|1, INF, 0), task1::add(v<<1|1, u<<1, 0, 0);
				mp[{u, v}]=1;
			}
			// cout<<"now: "<<now<<endl;
			for (int i=1; i<=now; ++i) {
				if (!anynull[i]) {
					task1::add(s, i<<1, 1, 0), task1::add(i<<1, s, 0, 0);
					if (siz[i]>1) task1::add(i<<1, t, INF, 1), task1::add(t, i<<1, 0, -1);
				}
				task1::add(i<<1|1, t, 1, 0), task1::add(t, i<<1|1, 0, 0);
				task1::add(i<<1|1, t, INF, 1), task1::add(t, i<<1|1, 0, -1);
			}
		}
	}
	bool spfa(int s, int t) {
		memset(dis, 0x3f, sizeof(dis));
		memset(back, -1, sizeof(back));
		queue<int> q;
		inc[s]=INF; dis[s]=0;
		q.push(s);
		while (q.size()) {
			int u=q.front(); q.pop();
			vis[u]=0;
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (e[i].flw && dis[u]+e[i].cst<dis[v]) {
					dis[v]=dis[u]+e[i].cst;
					back[v]=i; inc[v]=min(inc[u], e[i].flw);
					if (!vis[v]) q.push(v), vis[v]=1;
				}
			}
		}
		return ~back[t];
	}
	void solve() {
		memset(head, -1, sizeof(head)); ecnt=1;
		s=137, t=138;
		graph::tran();
		int ans1=0, ans2=0;
		memset(vis, 0, sizeof(vis)); // spfa
		while (spfa(s, t)) {
			ans1+=inc[t];
			ans2+=dis[t]*inc[t];
			for (int u=t; u!=s; u=e[back[u]^1].to) {
				e[back[u]].flw-=inc[t];
				e[back[u]^1].flw+=inc[t];
			}
		}
		// cout<<"ans1: "<<ans1<<endl;
		printf("%d\n", sum-ans2);
	}
}

namespace task2{
	bool vis[150];
	int dis[150], inc[150], back[150];
	int head[150], ecnt, s, t, tot, sum, sum2;
	struct edge{int to, next, flw, cst;}e[N<<1];
	inline void add(int s, int t, int f, int c) {/* cout<<"add: "<<s<<' '<<t<<' '<<f<<' '<<c<<endl; */ e[++ecnt]={t, head[s], f, c}; head[s]=ecnt;}
	namespace graph{
		int in[150], out[150];
		bool exist[150], anynull[150], ins[150];
		int head[150], dfn[150], low[150], siz[150], esiz[150], bel[150], sta[150], top, ecnt, now, tot;
		vector<pair<int, int>> esta;
		map<pair<int, int>, bool> mp;
		struct edge{int to, next;}e[N<<1];
		inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}
		inline int id(char c) {
			if (islower(c)) return c-'a'+1;
			else if (isupper(c)) return c-'A'+27;
			else return c-'0'+53;
		}
		void tarjan(int u) {
			// cout<<"tarjan: "<<u<<endl;
			dfn[u]=low[u]=++tot;
			ins[sta[++top]=u]=1;
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (!dfn[v]) {
					tarjan(v);
					low[u]=min(low[u], low[v]);
				}
				else if (ins[v]) low[u]=min(low[u], dfn[v]);
			}
			if (dfn[u]==low[u]) {
				++now;
				do {
					++siz[now];
					ins[sta[top]]=0;
					if (!exist[sta[top]]) anynull[now]=1, ++esiz[now];
					bel[sta[top--]]=now;
				} while (sta[top+1]!=u);
			}
		}
		void tran() {
			ecnt=now=tot=top=sum=sum2=0;
			mp.clear();
			memset(head, -1, sizeof(head));
			memset(exist, 0, sizeof(exist));
			memset(dfn, 0, sizeof(dfn));
			memset(low, 0, sizeof(low));
			memset(ins, 0, sizeof(ins));
			memset(siz, 0, sizeof(siz));
			memset(in, 0, sizeof(in));
			memset(esiz, 0, sizeof(esiz));
			memset(out, 0, sizeof(out));
			memset(anynull, 0, sizeof(anynull));
			memset(bel, 0, sizeof(bel));
			esta.clear();
			for (int i=1; i<=n; ++i) exist[id(str[i])]=1;
			for (int i=1; i<=62; ++i) if (exist[i]) ++sum;
			// cout<<"sum: "<<sum<<endl;
			for (int i=1; i<=m; ++i) {
				int u=id(g[i].fir), v=id(g[i].sec);
				if (u==v) continue;
				add(u, v); esta.pb({u, v});
			}
			for (int i=1; i<=62; ++i) if (!dfn[i]) tarjan(i);
			for (auto it:esta) {
				if (bel[it.fir]==bel[it.sec] || mp.find({bel[it.fir], bel[it.sec]})!=mp.end()) continue;
				int u=bel[it.fir], v=bel[it.sec];
				task2::add(u<<1|1, v<<1, INF, 0), task2::add(v<<1, u<<1|1, 0, 0);
				// task2::add(u<<1, v<<1|1, INF, 0), task2::add(v<<1|1, u<<1, 0, 0);
				// cout<<"add: "<<u<<' '<<v<<endl;
				mp[{u, v}]=1;
				++in[v]; ++out[u];
			}
			// cout<<"now: "<<now<<endl;
			// for (int i=1; i<=now; ++i) cout<<setw(2)<<siz[i]<<' '; cout<<endl;
			// for (int i=1; i<=now; ++i) cout<<setw(2)<<anynull[i]<<' '; cout<<endl;
			// for (int i=1; i<=now; ++i) cout<<setw(2)<<in[i]<<' '; cout<<endl;
			// for (int i=1; i<=now; ++i) cout<<setw(2)<<out[i]<<' '; cout<<endl;
			// for (int i=1; i<=now; ++i) cout<<setw(2)<<esiz[i]<<' '; cout<<endl;
			for (int i=1; i<=now; ++i) {
				if (!anynull[i]) {
					if (siz[i]>1 || (siz[i]==1&&out[i])) ++sum2;
					task2::add(s, i<<1|1, 1, 0), task2::add(i<<1|1, s, 0, 0);
					if (siz[i]>1 || (siz[i]==1&&out[i])) task2::add(i<<1, t, 1, 0), task2::add(t, i<<1, 0, 0);
				}
				else task2::add(i<<1, t, esiz[i], 0), task2::add(t, i<<1, 0, 0);
				task2::add(i<<1, i<<1|1, INF, 0), task2::add(i<<1|1, i<<1, 0, 0);
				// task2::add(i<<1|1, t, INF, 1), task2::add(t, i<<1|1, 0, -1);
			}
		}
	}
	bool spfa(int s, int t) {
		memset(dis, 0x3f, sizeof(dis));
		memset(back, -1, sizeof(back));
		queue<int> q;
		inc[s]=INF; dis[s]=0;
		q.push(s);
		while (q.size()) {
			int u=q.front(); q.pop();
			vis[u]=0;
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (e[i].flw && dis[u]+e[i].cst<dis[v]) {
					dis[v]=dis[u]+e[i].cst;
					back[v]=i; inc[v]=min(inc[u], e[i].flw);
					if (!vis[v]) q.push(v), vis[v]=1;
				}
			}
		}
		return ~back[t];
	}
	void solve() {
		memset(head, -1, sizeof(head)); ecnt=1;
		s=137, t=138;
		graph::tran();
		int ans1=0, ans2=0;
		memset(vis, 0, sizeof(vis)); // spfa
		while (spfa(s, t)) {
			ans1+=inc[t];
			ans2+=dis[t]*inc[t];
			for (int u=t; u!=s; u=e[back[u]^1].to) {
				e[back[u]].flw-=inc[t];
				e[back[u]^1].flw+=inc[t];
			}
		}
		// cout<<"sum2: "<<sum2<<endl;
		// cout<<"ans1: "<<ans1<<endl;
		printf("%d\n", sum-(sum2-ans1));
	}
}

signed main()
{
	freopen("graph.in", "r", stdin);
	freopen("graph.out", "w", stdout);

	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%s%d", str+1, &m);
		n=strlen(str+1); top=0;
		for (int i=1; i<=m; ++i) scanf("%s", tr), g[++top]={tr[0], tr[1]};
		// task1::solve();
		task2::solve();
	}
	
	return 0;
}
posted @ 2022-03-08 18:56  Administrator-09  阅读(2)  评论(0编辑  收藏  举报