首页 > 其他分享 >CodeForces 1525F Goblins And Gnomes

CodeForces 1525F Goblins And Gnomes

时间:2023-07-11 17:13:24浏览次数:58  
标签:Goblins int flow CodeForces len edges maxn ans 1525F

洛谷传送门

CF 传送门

套路地,将 DAG 的最小不交路径覆盖转化为点数减去拆点以后最大匹配。感性理解就是一开始每个点都是一条路径,一个匹配意味着两条路径结合了。

由题意知,第 \(i\) 次进攻时最小不交路径覆盖必须 \(> i\),也就是说,设最大匹配为 \(k\),那么 \(n - k > i\),即 \(k \le n - i - 1\)。

同时,通过上面的转化,题中删除某个点所有入边或出边的操作也可以转化为,在二分图上删去一个点,左右都可以。

我们发现,只要 \(k > 0\),我们总能找到一个点,使得删掉后 \(k \gets k - 1\)。因为由 Konig 定理得二分图最大匹配 \(=\) 最小点覆盖,只要从最小点覆盖中随意删除一个点即可。

于是我们可以求出一个删点序列,满足依次删除序列中的点,\(k\) 都能依次减 \(1\)。

既然我们已经能够满足任意次删点了,那我们就可以对着每次进攻的参数 \(a_i, b_i\) 做一个 dp,求出最大收益。输出方案就直接依次输出上面求出的删点序列即可。

时间复杂度大概是 \(O(n^5)\)?

code
// Problem: F. Goblins And Gnomes
// Contest: Codeforces - Educational Codeforces Round 109 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1525/F
// Memory Limit: 512 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 110;
const int maxm = 10000;
const int inf = 0x3f3f3f3f;

int n, m, K, a[maxn], id[maxn][2], ntot, S, T, head[maxn], len, di[maxn];
ll f[maxn][maxn], g[maxn][maxn];

bool vis[maxn];
pii E[maxm];

struct edge {
	int to, next, cap, flow;
} edges[maxm];

inline void add_edge(int u, int v, int c, int f) {
	edges[++len].to = v;
	edges[len].next = head[u];
	edges[len].cap = c;
	edges[len].flow = f;
	head[u] = len;
}

struct Dinic {
	int d[maxn], cur[maxn];
	bool vis[maxn];
	
	inline void add(int u, int v, int c) {
		add_edge(u, v, c, 0);
		add_edge(v, u, 0, 0);
	}
	
	inline bool bfs() {
		for (int i = 1; i <= ntot; ++i) {
			vis[i] = 0;
			d[i] = -1;
		}
		queue<int> q;
		q.push(S);
		d[S] = 0;
		vis[S] = 1;
		while (q.size()) {
			int u = q.front();
			q.pop();
			for (int i = head[u]; i; i = edges[i].next) {
				edge &e = edges[i];
				if (!vis[e.to] && e.cap > e.flow) {
					vis[e.to] = 1;
					d[e.to] = d[u] + 1;
					q.push(e.to);
				}
			}
		}
		return vis[T];
	}
	
	int dfs(int u, int a) {
		if (u == T || !a) {
			return a;
		}
		int flow = 0, f;
		for (int &i = cur[u]; i; i = edges[i].next) {
			edge &e = edges[i];
			if (d[e.to] == d[u] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) {
				e.flow += f;
				edges[i ^ 1].flow -= f;
				flow += f;
				a -= f;
				if (!a) {
					break;
				}
			}
		}
		return flow;
	}
	
	inline int solve() {
		int flow = 0;
		while (bfs()) {
			for (int i = 1; i <= ntot; ++i) {
				cur[i] = head[i];
			}
			flow += dfs(S, inf);
		}
		return flow;
	}
} solver;

inline int work() {
	len = 1;
	mems(head, 0);
	for (int i = 1; i <= n; ++i) {
		if (!vis[id[i][0]]) {
			solver.add(S, id[i][0], 1);
		}
		if (!vis[id[i][1]]) {
			solver.add(id[i][1], T, 1);
		}
	}
	for (int i = 1; i <= m; ++i) {
		int u = E[i].fst, v = E[i].scd;
		if (!vis[id[u][0]] && !vis[id[v][1]]) {
			solver.add(id[u][0], id[v][1], 1);
		}
	}
	return solver.solve();
}

void solve() {
	scanf("%d%d%d", &n, &m, &K);
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d", &E[i].fst, &E[i].scd);
	}
	for (int i = 1; i <= n; ++i) {
		id[i][0] = ++ntot;
		di[ntot] = i;
		id[i][1] = ++ntot;
		di[ntot] = -i;
	}
	S = ++ntot;
	T = ++ntot;
	int flow = work();
	for (int i = 1; i <= flow; ++i) {
		for (int j = 1; j <= n * 2; ++j) {
			if (!vis[j]) {
				vis[j] = 1;
				if (work() == flow - i) {
					a[i] = j;
					break;
				}
				vis[j] = 0;
			}
		}
	}
	mems(f, -0x3f);
	f[0][0] = 0;
	for (int i = 1; i <= K; ++i) {
		ll x, y;
		scanf("%lld%lld", &x, &y);
		for (int j = 0; j <= flow; ++j) {
			if (n - (flow - j) <= i) {
				continue;
			}
			for (int k = 0; k <= j; ++k) {
				if (f[i][j] < f[i - 1][k] + max(0LL, x - y * (j - k))) {
					f[i][j] = f[i - 1][k] + max(0LL, x - y * (j - k));
					g[i][j] = k;
				}
			}
		}
	}
	vector<int> ans;
	ll p = 0;
	for (int i = 1; i <= flow; ++i) {
		if (f[K][i] > f[K][p]) {
			p = i;
		}
	}
	for (int i = K, j = p; i; j = g[i--][j]) {
		ans.pb(0);
		for (int k = j; k > g[i][j]; --k) {
			ans.pb(di[a[k]]);
		}
	}
	reverse(ans.begin(), ans.end());
	printf("%d\n", (int)ans.size());
	for (int x : ans) {
		printf("%d ", x);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:Goblins,int,flow,CodeForces,len,edges,maxn,ans,1525F
From: https://www.cnblogs.com/zltzlt-blog/p/17545333.html

相关文章

  • E. Two Chess Pieces -- (codeforces) 树形DP
    原题链接:https://codeforces.com/contest/1774/problem/E题意:两颗棋子,给出两颗棋子必须要去的顶点,且给出两颗棋子的相隔距离不能大于d,算出两颗棋子完成目标后走的距离。最后两颗棋子都要回到顶点1上。思路:刚开始没想出来,顺着官方题解写的,大意就是我用数组s1和s2代表两颗棋子......
  • CodeForces 1508C Complete the MST
    洛谷传送门AtCoder传送门比较需要观察的题。设\(v\)为所有边权异或和。直觉是设一条未确定权值的边边权为\(v\),其他的为\(0\)最优。证明大概就是讨论MST是否全部使用未确定权值的边。若全使用了,那么根据\(\oplusw\le\sumw\)可知\(\min\sumw=\oplusw\),并且......
  • CodeForces 997C Sky Full of Stars
    洛谷传送门CF传送门考虑容斥,钦定\(i\)行\(j\)列放同一种颜色,其余任意。\(i=0\)或\(j=0\)的情况平凡,下面只考虑\(i,j\ne0\)的情况。答案为:\[\sum\limits_{i=1}^n\sum\limits_{j=1}^n(-1)^{i+j+1}\binom{n}{i}\binom{n}{j}3^{(n-i)(n-j)+1}......
  • Codeforces Round 883 (Div. 3)
    CodeforcesRound883(Div.3)A.RudolphandCuttheRope:题意:有一个糖果由n个绳子悬挂,告诉每一个绳子位于的高度和宽度,问至少间断几根才可以让candy回到groud。思路:统计有几个宽度小于高度的绳子即可voidsolve(){intn;intnum=0;cin>>n;for(inti=1;i......
  • Codeforces Round 875 (Div. 2)(D)
    CodeforcesRound875(Div.2)(D)D(思维)这个题意是给你两个数组,\(a\)和\(b\),我们需要找到这样的二元组\((i,j)\)满足\(a_i\timesa_j=b_i+b_j\),问一共有多少组满足以上条件的二元组题目还告诉我们数组里面的数字都是不大于\(n\)的,那么因为两个数相乘的范围一定是\(1-n\)的,那......
  • Codeforces Round 882 (Div. 2)
    Preface这场现场打的,顶着第二天一早起来军训硬打到一点这场题目都是JOJO确实好评,但刚开始的评测姬爆让人很难顶啊,因为这个B题挂了一发没法第一时间改导致这场罚时裂开了这场写完D还有快50min,然后看一眼榜E出的人很少但是F好多人过然后就去想F,由于军训生物钟的缘故当时好困好困......
  • Codeforces Round 882 (Div. 2) C. Vampiric Powers, anyone?
    由题目观察可得,a[m+1]=a[i]^...a[m],,结合异或的性质a^b^a=b,可得如果在末尾添加一个a[m+1],a[m+1]会和末尾几个抵消掉,求得i~k这一段的异或和,k<m,因此通过该操作实际上我就可以求得所有长度连续区间的异或和,求其最大值,n=1e5+10,如果暴力求解肯定会超时,我们观察发现a[i]的范围为0~2^8......
  • Codeforces Round 882 (Div. 2) A-D
    ATheManwhobecameaGod 假设sum为omigaabs(a[i]-a[i-1])1<=i<=n 只有设置断点的时候,假设设置在t和t-1之间thevalue才会减少abs(a[t]-a[t-1]) 所以把差距最大的几个地方分段就行了#include<bits/stdc++.h>usingnamespacestd;#definemaxn400100#defi......
  • Educational Codeforces Round 151 (Rated for Div. 2) D. Rating System
    贪心由题可得,对于k的选择一定是单调递增的,对于前面选定的k后面选的k必须大于之前选的才会发生新的变化,因此k的选择其实是一个单调栈,由前缀和组成我们要想最后的结果最大,则k值一定要尽可能的高,例如当选中i为k值时,如果从i后面某个原本的前缀和要大于选k之后所得到的前缀和的话,说明......
  • Educational Codeforces Round 151 (Rated for Div. 2) C. Strong Password
    题目翻译,给定t组数据,每组数据包含一个字符串s,两个长度为m的字符串l和r,要求判断是否存在一个长度为m的字符串res,满足l[i]<=res[i]<=r[i](i->0~m)且不是s的子序列贪心首先对于所有满足l<res<r的字符串,我们只需判断是否存在一个字符串不是子序列即可,那么我们让res成为子序列的可能......