【AtCoder】AGC038重練題解

AtCoder AGC038 題解

只寫了一部分QAQ。。。

剩下的坑後面再填吧。。。

A.01 Matrix

題目大意

要求構造一個N×MN\times M的01矩陣,其中每行0或者1中較少者的數量恰好爲AA,每列0或1中較少者的數量恰好爲BB。求這樣一個矩陣。

分析

按如下方法構造即可:

參考代碼

#include <cstdio>
#include <algorithm>
using namespace std;

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	int N, M, A, B;
	scanf("%d %d %d %d", &N, &M, &A, &B);
	for(int i = 1; i <= N; i++) {
		for(int j = 1; j <= M; j++)
			if((i <= B && j <= A) || (i > B && j > A))
				putchar('1');
			else putchar('0');
		putchar('\n');
	}
	return 0;
}

D.Unique Path

題目大意

給定KK條信息,每條信息要麼表示圖上u,vu,v之間只有一條簡單路徑或者多條路徑。求是否存在一個NN個點MM條邊的連通圖符合所有的信息。

分析

考慮給定的信息中的第一類,即只包含一條簡單路徑。

我們直接將這兩個點連起來就可以了。

這樣一來,整張圖目前就是一個森林。如果某個連通塊中有第二類信息出現,那麼是不合法的。

考慮將第二類信息加進來。

設已經產生了cc個連通塊,那麼最多需要在所有的連通塊之間連上邊,總邊數爲c(c+1)2+Nc\frac{c(c+1)}{2}+N-c

如果MM比這個還大的話,那麼就肯定無法構造出這個圖。

考慮最小的邊數。如果有第二類信息,那麼最小邊數爲NN,否則爲N1N-1。如果MM比這還小的話,那麼也無法構造出這個圖。

參考代碼

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

typedef long long ll;
const int Maxn = 1e5;

int N, K;
ll M;
vector<pair<int, int> > t;

int fa[Maxn + 5];
int find(int x) {
	if(x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	scanf("%d %lld %d", &N, &M, &K);
	for(int i = 1; i <= N; i++)
		fa[i] = i;
	for(int i = 1; i <= K; i++) {
		int u, v, typ;
		scanf("%d %d %d", &u, &v, &typ);
		u++, v++;
		if(typ == 0) {
			int fu = find(u), fv = find(v);
			if(fu != fv) fa[fu] = fv;
		} else t.push_back(make_pair(u, v));
	}
	int cnt = 0;
	for(int i = 1; i <= N; i++)
		if(i == find(i)) cnt++;
	ll mx = 1LL * cnt * (cnt - 1) / 2 + N - cnt;
	if(mx < M) {
		puts("No");
		return 0;
	}
	ll mn = (t.empty() ? N - 1 : N);
	if(mn > M) {
		puts("No");
		return 0;
	}
	for(int i = 0; i < (int)t.size(); i++)
		if(find(t[i].first) == find(t[i].second)) {
			puts("No");
			return 0;
		}
	puts("Yes");
	return 0;
}

F.Two Permutations

題目大意

給定兩個11NN的排列A,BA,B,現在可以將AiA_i變成ii,或者BiB_i變成ii,要求變換後仍然是一個11NN的排列。問變換後最多位置不相同的個數。

分析

我們先將置換AA分解成多個循環。那麼對於每個循環,要麼保持AiA_i不變,要麼就全都變成ii

而每個元素的選擇能夠互相影響,而這樣就很容易想到網絡流上去。

考慮將問題等價轉化:求最少的位置相同的個數。

考慮按照如下方式構圖:(如果我們用b表示選i,而c表示選Bi的話,那麼我們解方程就會解出負數)

列出方程:{a+b=[Ai=i]c+d=[Bi=i]a+f+c=1b+e+d=1\begin{cases}a+b=[A_i=i]\\c+d=[B_i=i]\\a+f+c=1\\b+e+d=1\end{cases}

考慮Ai,Bi,iA_i,B_i,i的不同關係所產生的代價:

  • Ai=Bi=iA_i=B_i=i,此時代價無論如何都是11,直接減掉而不連邊;
  • Ai=i,BiiA_i=i,B_i\neq i,利用特殊值帶入方程,解得b=1b=1,其餘都是00
  • Aii,Bi=iA_i\neq i, B_i=i,利用特殊值,解得d=1d=1,其餘爲00
  • Ai=BiiA_i=B_i\neq i,利用特殊值,解得e=f=1e=f=1,其餘爲00
  • AiBiiA_i\neq B_i\neq i,利用特殊值,解得e=1e=1,其餘爲00

建圖跑網絡流最小割即可。

另外注意對於一個循環,我們建一個虛點,從虛點向循環裏的每個節點連一條容量爲\infty的邊,表示無法將這些循環分開。

參考代碼

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int Maxn = 100000;
const int INF = 0x3f3f3f3f;

struct MaxFlow_Dinic {
	struct Edge {
		int to, cap;
		Edge *nxt, *bak;
	};
	Edge pool[Maxn * 40 + 5];
	Edge *ecnt, *G[Maxn * 8 + 5];
	inline void addedge(int u, int v, int cap) {
		Edge *p = ++ecnt;
		p->to = v, p->cap = cap;
		p->nxt = G[u], G[u] = p;
		p->bak = p + 1;
		p = ++ecnt;
		p->to = u, p->cap = 0;
		p->nxt = G[v], G[v] = p;
		p->bak = p - 1;
	}
	int node, s, t;
	int d[Maxn * 8 + 5];
	Edge *cpy[Maxn * 8 + 5];
	void init(int n) {
		ecnt = &pool[0];
		node = n;
		for(int i = 1; i <= n; i++)
			G[i] = NULL;
	}
	int dfs(int u, int tot) {
		if(u == t) return tot;
		int sum = 0;
		for(;cpy[u] != NULL; cpy[u] = cpy[u]->nxt) {
			int v = cpy[u]->to;
			if(d[v] == d[u] + 1 && cpy[u]->cap > 0) {
				int delta = dfs(v, min(tot - sum, cpy[u]->cap));
				cpy[u]->cap -= delta, cpy[u]->bak->cap += delta;
				sum += delta;
				if(sum == tot) break;
			}
		}
		return sum;
	}
	bool bfs(int _s, int _t) {
		for(int i = 0 ; i <= node; i++)
			d[i] = -1, cpy[i] = G[i];
		queue<int> q;
		q.push(_s), d[_s] = 1;
		while(!q.empty()) {
			int u = q.front();
			q.pop();
			for(Edge *p = G[u]; p != NULL; p = p->nxt) {
				int v = p->to;
				if(d[v] == -1 && p->cap > 0) {
					d[v] = d[u] + 1;
					if(v == _t) return true;
					q.push(v);
				}
			}
		}
		return d[_t] != -1;
	}
	int dinic(int _s, int _t) {
		int ret = 0;
		s = _s, t = _t;
		while(bfs(s, t)) ret += dfs(s, INF);
		return ret;
	}
};

MaxFlow_Dinic f;
int N, A[Maxn + 5], B[Maxn + 5];
int vis[Maxn + 5];

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	scanf("%d", &N);
	f.init(N);
	int cnt = 2 * N;
	for(int i = 1; i <= N; i++)
		scanf("%d", &A[i]), A[i]++;
	for(int i = 1; i <= N; i++)
		if(vis[i] == false) {
			int p = i;
			cnt++;
			do {
				vis[p] = true;
				p = A[p];
				f.addedge(cnt, p, INF), f.addedge(p, cnt, INF);
			} while(p != i);
		}
	for(int i = 1; i <= N; i++)
		scanf("%d", &B[i]), B[i]++, vis[i] = false;
	for(int i = 1; i <= N; i++)
		if(vis[i] == false) {
			int p = i;
			cnt++;
			do {
				vis[p] = true;
				p = B[p];
				f.addedge(cnt, p + N, INF), f.addedge(p + N, cnt, INF);
			} while(p != i);
		}
	int s = cnt + 1, t = cnt + 2;
	int ans = N;
	f.node = cnt + 2;
	for(int i = 1; i <= N; i++) {
		if(i == A[i] && i == B[i] && A[i] == B[i])
			ans--;
		if(i == A[i] && i != B[i] && A[i] != B[i])
			f.addedge(s, i + N, 1);
		if(i != A[i] && i == B[i] && A[i] != B[i])
			f.addedge(i, t, 1);
		if(i != A[i] && i != B[i] && A[i] == B[i])
			f.addedge(i, i + N, 1), f.addedge(i + N, i, 1);
		if(i != A[i] && i != B[i] && A[i] != B[i])
			f.addedge(i, i + N, 1);
	}
	printf("%d\n", ans - f.dinic(s, t));
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章