2019-2020 ICPC, NERC, Southern and Volga Russian Regional Contest

[TOC]node

Contest Info

<hr> [Practice Link](https://codeforces.com/contest/1250)ios

Solved A B C D E F G H I J K L M N
9/14 O O O - O O - O - O - O - O
  • O 在比賽中經過
  • Ø 賽後經過
  • ! 嘗試了可是失敗了
  • - 沒有嘗試

Solutions

<hr>c++

A. Berstagram

題意: 給出$n$個數,剛開始第$i$個數在第$i$個位置,有$m$次操做,將標號爲$a_i$的數和它前面那個數交換位置,若是它已經在最前面了,那麼不操做。 最後輸出$n$行,表示每一個數所待過的位置的下標的最小值和最大值app

思路: 每次交換隻會影響兩個數,暴力便可。ide

代碼:ui

<details> <summary>view code</summary> ```c++ #include <bits/stdc++.h> using namespace std; using pII = pair<int, int>; #define fi first #define se second const int N = 4e5 + 10; int n, m, a[N], fa[N], b[N]; pII res[N]; void up(int x, int y) { res[x].fi = min(res[x].fi, y); res[x].se = max(res[x].se, y); }spa

int main() { while (scanf("%d%d", &n, &m) != EOF) { for (int i = 1; i <= n; ++i) a[i] = i, fa[i] = i, res[i] = pII(i, i); for (int i = 1; i <= m; ++i) scanf("%d", b + i); for (int i = 1; i <= m; ++i) { int x = b[i]; if (fa[x] == 1) continue; int pre = a[fa[x] - 1]; swap(fa[x], fa[pre]); swap(a[fa[x]], a[fa[pre]]); up(x, fa[x]); up(pre, fa[pre]); // for (int j = 1; j <= n; ++j) // printf("%d%c", a[j], " \n"[j == n]); } for (int i = 1; i <= n; ++i) printf("%d %d\n", res[i].fi, res[i].se); } return 0; }code

</details>


### B. The Feast and the Bus

題意:
有$n$我的,$k$個小組,每一個人屬於一個小組,每一個小組至少有一我的。
如今要租$r$輛巴士,每輛巴士的容量都爲$s$,可是$s$和$r$能夠本身定,使得可以裝下全部人,而且知足如下兩個限制條件:
- 同一組的人在同一輛巴士
- 一輛巴士最多有兩個小組的人

使得$r \cdot s$最小

思路:
考慮$k$很小,咱們能夠枚舉$r$,而後能夠算出有多少輛巴士必需要兩個小組,而後貪心放,讓小組人數多的佔用單組巴士,小組人數少的貪心配對,即最大的配最小的,次大的配次小的$\cdots$
時間複雜度$O(k^2)$

代碼:
<details>
<summary>view code</summary>

```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e5 + 10;
int n, k, a[N];
ll gao(int x) {
	ll s = 0;
	int need = k - (2 * x - k);
	for (int i = 1, j = need; i < j; ++i, --j) {
		s = max(s, 1ll * a[i] + a[j]);
	}
	for (int i = need + 1; i <= k; ++i)
		s = max(s, 1ll * a[i]);
	return s;
}

int main() {
	while (scanf("%d%d", &n, &k) != EOF) {
		memset(a, 0, sizeof a);
		for (int i = 1, x; i <= n; ++i) {
			scanf("%d", &x);
			++a[x];
		}
		sort(a + 1, a + 1 + k);
		ll res = 1e18;
		for (int i = (k + 1) / 2; i <= k; ++i) {
			res = min(res, 1ll * i * gao(i));
		}
		printf("%lld\n", res);
	}
	return 0;
}

</details>blog

C. Trip to Saint Petersburg

題意: 給出$n$個工做,和一個參數$k$。 每一個工做的工做時間爲$[l_i, r_i]$,能夠得到$p_i$的利潤,而且工做隨便選,工做時間能夠重疊。 惟一的代價就是所選擇的工做中的最小的$L = l_i$,最大的$R = r_i$,代價就是$k \cdot (R - L + 1)$。 問所能得到的最大利潤。遊戲

思路: 枚舉右端點$R$,而後線段樹維護左端點的貢獻,每次要將$r_i = R$的工做的貢獻加給左端點在$[1, l_i]$範圍內的。 而後查詢區間最值便可。

代碼:

<details> <summary>view code</summary>

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pIL = pair<int, ll>;
#define fi first
#define se second
const int N = 2e5 + 10;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, m, pl[N], pr[N]; ll k;
vector <vector<pIL>> vec;

struct SEG {
	struct node {
		ll Max, lazy; int pos; 
		node() { Max = -INF; lazy = pos = 0; } 
		void up(ll x) {
			Max += x;
			lazy += x;
		}
		node operator + (const node &other) const {
			node res = node();
			if (Max >= other.Max) {
				res.Max = Max;
				res.pos = pos;
			} else {
				res.Max = other.Max;
				res.pos = other.pos;
			}
			return res;
		}
	}t[N << 2], res;
	void build(int id, int l, int r) {
		t[id] = node();
		if (l == r) {
			t[id].Max = 0;
			t[id].pos = l;
			return;
		}
		int mid = (l + r) >> 1;
		build(id << 1, l, mid);
		build(id << 1 | 1, mid + 1, r);
		t[id] = t[id << 1] + t[id << 1 | 1];
	}
	void down(int id) {
		ll &lazy = t[id].lazy;
		if (lazy) {
			t[id << 1].up(lazy);
			t[id << 1 | 1].up(lazy);
			lazy = 0;
		}
	}
	void update(int id, int l, int r, int ql, int qr, ll v) {
		if (l >= ql && r <= qr) {
			t[id].up(v);
			return;
		}
		int mid = (l + r) >> 1;
		down(id);
		if (ql <= mid) update(id << 1, l, mid, ql, qr, v);
		if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr, v);
		t[id] = t[id << 1] + t[id << 1 | 1];
	}
	void query(int id, int l, int r, int ql, int qr) {
		if (l >= ql && r <= qr) {
			res = res + t[id];
			return;
		}
		int mid = (l + r) >> 1;
		down(id);
		if (ql <= mid) query(id << 1, l, mid, ql, qr);
		if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr);
	}
}seg;

int main() {
	while (scanf("%d%lld", &n, &k) != EOF) {
		vec.clear(); vec.resize(N);
		m = 2e5;
		for (int i = 1; i <= n; ++i) {
			int l, r; ll p;
			scanf("%d%d%lld", &l, &r, &p);
			pl[i] = l, pr[i] = r;
			vec[r].push_back(pIL(l, p));
		}
		ll p = 0; int L = -1, R = -1; 
		seg.build(1, 1, m);
		for (int i = 1; i <= m; ++i) {
			seg.update(1, 1, m, 1, i, -k);
			for (auto &it : vec[i]) 
				seg.update(1, 1, m, 1, it.fi, it.se);
			seg.res = SEG::node();
			seg.query(1, 1, m, 1, i);
			if (seg.res.Max > p) {
				p = seg.res.Max;
				L = seg.res.pos;
				R = i;
			}
		}		
		if (p == 0) puts("0");
		else {
			vector <int> vec;
			for (int i = 1; i <= n; ++i) 
				if (pl[i] >= L && pr[i] <= R)
					vec.push_back(i);
			int sze = vec.size();
			printf("%lld %d %d %d\n", p, L, R, sze);
			for (int i = 0; i < sze; ++i)
				printf("%d%c", vec[i], " \n"[i == sze - 1]);
		}
	}
	return 0;
}

</details>

E. The Coronation

題意: 給出$n$個$01$串,每一個$01$串能夠$reverse$,求最少的$reverse$次數,使得任意兩個串的有大於等於$k$個位置的字符是相同的。

代碼:

<details> <summary>view code</summary>

#include <bits/stdc++.h>

using namespace std;

const int N = 60;

struct Edge {
	int v, p;//1 same

	Edge() {}

	Edge(int v, int p): v(v), p(p) {}
};

bool F;
int n, m, k;
string s[N];
vector<vector<Edge> > G;

bool ok(const string &S, const string &T) {
	int cnt = 0;
	for (int i = 0; i < m; ++i) {
		if (S[i] == T[i]) ++cnt;
	}
	return cnt >= k;
}

int col[N], vis[N];
vector<int> vec, res;

void DFS(int u) {
	if (!F) return ;
	vis[u] = 1;
	vec.push_back(u);
	for (auto &it: G[u]) {
		if (col[it.v] == -1) {
			if (it.p) {
				col[it.v] = col[u];
			} else {
				col[it.v] = col[u] ^ 1;
			}
			DFS(it.v);
		} else { 
			if (it.p) {
				if (col[it.v] != col[u]) {
					F = false;
					break;
				}
			} else {
				if (col[it.v] != (col[u] ^ 1)) {
					F = false;
					break;
				}
			}
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		cin >> n >> m >> k;
		G.clear();
		G.resize(n + 1);
		memset(vis, 0, sizeof vis);
		memset(col, -1, sizeof col);
		for (int i = 1; i <= n; ++i) {
			cin >> s[i];
		}
		F = true;
		for (int i = 1; i <= n; ++i) {
			for (int j = i + 1; j <= n; ++j) {
				int cnt = 0;
				int same = 0;
				if (ok(s[i], s[j])) {
					cnt++;
					same = 1;
				}
				reverse(s[j].begin(), s[j].end());
				cnt += ok(s[i], s[j]);
				reverse(s[j].begin(), s[j].end());
				if (cnt == 0) {
					F = false;
					break;
				}
				if (cnt == 1) {
					G[i].push_back(Edge(j, same));
					G[j].push_back(Edge(i, same));
				}
			}
			if (!F) {
				F = false;
				break;
			}
		}
		if (!F) {
			cout << "-1\n";
			continue;
		}
		res.clear();
		for (int i = 1; i <= n; ++i) {
			if (!vis[i]) {
				col[i] = 1;
				vec.clear();
				DFS(i);
				int cnt[2] = {0, 0};
				for (auto &it: vec) {
					cnt[col[it]]++;
				}
				int now = 0;
				if (cnt[1] < cnt[0]) {
					now = 1;
				}
				for (auto &it : vec) {
					if (col[it] == now) {
						res.push_back(it);
					}
				}
			}
		}
		if (!F) {
			cout << "-1\n";
		} else {
			int sze = res.size();
			cout << sze << "\n";
			for (int i = 0; i < sze; ++i) {
				if (i) cout << " ";
				cout << res[i];
			}
			cout << "\n";
		}
	}
	return 0;
}

</details>

F. Data Center

題意: 給出一個矩形的面積$n$,求全部合法矩形中的最小周長。

思路: 暴力分解。

代碼:

<details> <summary>view code</summary>

#include <bits/stdc++.h>
using namespace std;

int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		int res = 1e9;
		for (int i = 1; i <= n; ++i) {
			if (n % i == 0) {
				res = min(res, i + n / i);
			}
		}
		res *= 2;
		printf("%d\n", res);
	}
	return 0;
}

</details>

G. Discarding Game

題意: 有兩我的玩遊戲,剛開始兩我的的分數都是$0$,每一輪,$A$的分數會加上$a_i$,$B$的分數會加上$b_i$,若是某我的的分數大於等於$k$,它就輸了,若是兩我的都大於等於$k$,兩我的都輸了。 若是最後過完了$n$輪,兩人的分數都小於$k$,那麼是平局。 贏的狀況是其中某我的輸了,那麼另外一我的就贏了。 如今$A$有超能力,它能夠在每一輪加分結束後按下一個按鈕,假定此時$A$的分數爲$x$,$B$的分數爲$y$, $A$的分數變成$max(0, x - y)$,$B$的分數變成$max(0, y - x)$。 如今求最少次數使得$A$贏了。

H. Happy Birthday

題意: 給出$[0, 9]$每種數字的個數,問最小的不能被拼出來的數是多少。

代碼:

<details> <summary>view code</summary>

#include <bits/stdc++.h>

using namespace std;

int a[100];

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		for (int i = 0; i < 10; ++i) scanf("%d", a + i);
		int Min = a[0] + 2;
		for (int i = 1; i < 10; ++i) Min = min(Min, a[i] + 1);
		if (Min == a[0] + 2) {
			printf("1");
			for (int i = 1; i <= a[0] + 1; ++i) printf("0");
			puts("");
		} else {
			for (int i = 1; i < 10; ++i) {
				if (Min == a[i] + 1) {
					for (int j = 1; j <= a[i] + 1; ++j) printf("%d", i);
					puts("");
					break;
				}
			}
		}
	}
	return 0;
}

</details>

J. The Parade

代碼:

<details> <summary>view code</summary>

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

int n;
ll k;
ll a[N], b[N];

bool check(ll x) {
	ll cnt = 0, remind = 0;
	for (int i = 1; i <= n; ++i) {
		b[i] = a[i];
		if (b[i] >= x - remind) {
			cnt++;
			b[i] -= x - remind;
			remind = 0;
		}
		cnt += b[i] / x;
		remind = b[i] % x;
		if (cnt >= k) return true;
	}
	return cnt >= k;
}

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d %lld", &n, &k);
		for (int i = 1; i <= n; ++i) {
			scanf("%lld", a + i);
		}
		ll l = 1, r = 1e17, res = 0;
		while (r - l >= 0) {
			ll mid = (l + r) >> 1;
			if (check(mid)) {
				res = mid;
				l = mid + 1;
			} else {
				r =  mid - 1;
			}
		}
		printf("%lld\n", res * k);
	}	
	return 0;
}

</details>

L. Divide The Students

題意: 有三類人,每類人有$a, b, c$個。 如今要將這三類人分紅三組,使得第一類和第三類人不能在同一組,而且使得全部組的最大人數最少。

思路: 令$a > c$,那麼將$c$單獨放在一組,將$a$均分紅兩組,而後$b$每次選一我的數最少的組放。

代碼:

<details> <summary>view code</summary>

#include <bits/stdc++.h>
using namespace std;

int main() {
	int _T; scanf("%d", &_T);
	while (_T--) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
	//	int res = max((a + b + c + 2) / 3, min(a, c));
	//	printf("%d\n", res);
		if (a < c) swap(a, c);
		int A[3] = {a / 2, a - a / 2, c};
		while (b) {
			sort(A, A + 3);
			++A[0];
			--b;
		}
		printf("%d\n", max(A[0], max(A[1], A[2])));
	}
	return 0;
}

</details>

M. SmartGarden

題意: 給出一個$n \cdot n$的矩形,其中對角線和對角線下面一條線是牆,其餘地方是蔬菜,相似這樣: 如今每次能夠選擇若干個行,若干個列,將這些行列相交的地方澆上水,次數最多爲$50$次,而且不能澆到牆,而且每棵蔬菜都要被澆到。

N. Wires

題意: 給出$n$條邊,點的標號在$[1, 10^9]$,如今能夠修改某條邊的某個端點,使得這$n$條邊所構成的圖是一個連通塊。 使得修改次數最少。

思路: 顯然最少修改次數爲連通塊個數 - 1。 隨便選取一個連通塊出來,讓其餘連通塊都連向這個連通塊。 而後考慮每一個連通塊裏:

  • 若是有$1$度頂點,直接改掉這個$1$度頂點
  • 那麼沒有$1$度頂點,那麼必然有環,隨便改掉環上的一條邊便可

代碼:

<details> <summary>view code</summary>

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
struct Hash {
	vector <int> a;
	void init() { a.clear(); }
	void add(int x) { a.push_back(x); }
	void gao() { sort(a.begin(), a.end()); a.erase(unique(a.begin(), a.end()), a.end()); }
	int get(int x) { return lower_bound(a.begin(), a.end(), x) - a.begin() + 1; }
}hs;
struct E {
	int u, v;
	E() {}
	E(int u, int v) : u(u), v(v) {}
}e[N]; 
struct node {
	int id, u, v;
};
vector <vector<node>> G;
vector <node> res;
int n, m, d[N], fa[N], vis[N], Insta[N], used[N], usede[N], F; 
int find(int x) { return fa[x] == 0 ? x : fa[x] = find(fa[x]); }
void merge(int u, int v) {
	u = find(u); v = find(v); 
	if (u != v) fa[u] = v;
} 
void dfs(int u) {
	used[u] = 1;
	Insta[u] = 1;
	for (auto &it : G[u]) if (!usede[it.id]) {
		usede[it.id] = 1;
		int v = it.v;
		if (Insta[v]) {
			if (!F) {
				res.push_back({it.id, hs.a[it.u - 1], hs.a[0]});
				F = 1;
				return;
			}
		}
		if (used[v] == 0) {
			dfs(v);
		}
		if (F) return;
	}
	Insta[u] = 0;
}

int main() {
	int _T; scanf("%d", &_T);
	while (_T--) {
		scanf("%d", &n);
		hs.init();
		for (int i = 1, u, v; i <= n; ++i) {
			scanf("%d%d", &u, &v);
			hs.add(u); hs.add(v);
			e[i] = E(u, v);
			usede[i] = 0;
		}
		hs.gao();
		m = hs.a.size();
		for (int i = 1; i <= m; ++i) {
			d[i] = fa[i] = 0;
			vis[i] = 0;
			Insta[i] = used[i] = 0;
		}
		G.clear(); G.resize(m + 1);
		for (int i = 1; i <= n; ++i) {
			e[i].u = hs.get(e[i].u);
			e[i].v = hs.get(e[i].v);
			++d[e[i].u];
			++d[e[i].v];
			merge(e[i].u, e[i].v);
			int u = e[i].u, v = e[i].v;
			G[u].push_back({i, u, v});
			G[v].push_back({i, v, u});
		}
		int rt = 1, frt = find(rt);
		vis[frt] = 1;
		res.clear();
		for (int i = 1; i <= n; ++i) {
			int &u = e[i].u, &v = e[i].v;
			if (d[u] > d[v]) swap(u, v);
			int fu = find(u);
		    if (vis[fu]) continue;	
			if (d[u] == 1) {
				res.push_back({i, hs.a[u - 1], hs.a[0]});
				vis[fu] = 1;
			}
		}
		for (int i = 1; i <= m; ++i) {
			int fi = find(i);
			if (vis[fi]) continue;
			F = 0;
			vis[fi] = 1;
			dfs(i);
		}
		int sze = res.size();
		printf("%d\n", sze);
		for (int i = 0; i < sze; ++i) { 
			printf("%d %d %d\n", res[i].id, res[i].u, res[i].v);
		}
	}
	return 0;
}

</details>

相關文章
相關標籤/搜索