The 2019 China Collegiate Programming Contest Harbin Site

Contest Info


[Practice Link](https://codeforces.com/gym/102394)
Solved A B C D E F G H I J K L
7/12 O - - - O O - - O O O Ø
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Artful Paintings

题意:
\(n\)个小球,可以选择给一些小球染色,但是需要满足以下限制条件:

  • 对于第\(i\)个第一类限制,需要满足标号在\([L_i, R_i]\)范围的小球被染色的个数需要大于等于\(k_i\)
  • 对于第\(i\)个第二类限制,需要满足标号在\([L_i, R_i]\)范围外的小球被染色的个数需要大于等于\(k_i\)

求最小的小球个数。

思路:
\(S_i\)表示前\(i\)个小球的被染色数量,如下建图:

  • \(S_i - S_{i - 1} \geq 0\)
  • \(S_i - S_{i - 1} \leq 1\)
  • 第一类限制:\(S_{R_i} - S_{L_i - 1} \geq k_i\)
  • 第二类限制:\(S_n - (S_{R_i} - S_{L_i - 1}) \geq k_i\)

容易发现总的小球个数\(S_n\)具有单调性,二分,然后差分约束建图判断即可。

代码:

view code
#include <bits/stdc++.h>
using namespace std;
using pII = pair <int, int>;
#define fi first
#define se second
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int n, M1, M2, Maxk;
int dis[N], inq[N], cnt[N];
int que[N], ql, qr;
struct Edge {
	int v, w;
	Edge() {}
	Edge(int v, int w): v(v), w(w) {}
	bool operator < (const Edge &other) const {
		return w < other.w;
	}
};
vector <vector<Edge>> G;
struct node {
	int l, r, k;
	void input() {
		scanf("%d %d %d", &l, &r ,&k);
		Maxk = max(Maxk, k);
	}
}q1[N], q2[N];

inline void Init() {
	G.clear(); G.resize(n + 1);
	for (int i = 0; i <= n; ++i) {
		inq[i] = 0, cnt[i] = 0, dis[i] = INF;
	}
}

inline void addedge(int u, int v, int w) {
	G[u].push_back(Edge(v, w));
}

bool SPFA(int x) {
	for (int i = 0; i <= n; ++i) {
		inq[i] = cnt[i] = 0;
		dis[i] = INF;
	}
	dis[0] = 0;
	inq[0] = 1;
	queue <int> q;
	q.push(0);
	int limit = min(n, max(10, n / 2));
	while (!q.empty()) {
		int u = q.front(); q.pop();
		inq[u] = 0;
		for (auto &it : G[u]) {
			int v = it.v, w = it.w;
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				if (!inq[v]) {
					inq[v] = 1;
					q.push(v);
					if (++cnt[v] > limit) return false;
				}
			}
		}
	}
	return dis[n] == x;
}

bool check(int x) {
	G.clear(); G.resize(n + 1);
	for (int i = 1; i <= M1; ++i) {
		int l = q1[i].l - 1, r = q1[i].r, k = q1[i].k;
		addedge(r, l, -k);
	}
	for (int i = 1; i <= M2; ++i) {
		int l = q2[i].l - 1, r = q2[i].r, k = q2[i].k;
		addedge(l, r, x - k);
	} 
	for (int i = 1; i <= n; ++i) {
		addedge(i, i - 1, 0);
		addedge(i - 1, i, 1);
	}
	addedge(0, n, x);
	addedge(n, 0, -x);
	return SPFA(x);
}

int main() {   
	int T;  
	scanf("%d", &T);  
	while (T--) {  
		scanf("%d %d %d", &n, &M1, &M2); 
	    Maxk = 0;	
		for (int i = 1; i <= M1; ++i) q1[i].input(); 
		for (int i = 1; i <= M2; ++i) q2[i].input(); 
		int l = Maxk, r = n - 1, res = n;
		while (r - l >= 0) {
			int mid = (l + r) >> 1;
			if (check(mid)) {
				r = mid - 1;
				res = mid;
			} else {
				l = mid + 1;
			}
		}
		printf("%d\n", res);
	}
	return 0;
}

E. Exchanging Gifts

题意:
\(n\)个序列,对于第\(i\)个序列,以以下两种方式之一给出:

  • \(S_i = [q_1, q_2, \cdots, q_k]\)
  • \(S_i = S_x + S_y\)

最后求\(S_n\)的最大欢乐值,这个欢乐值是重新排列序列中的数,某个位置\(i\),如果重排列后的数和原始的数不一样,那么这个位置提供一个欢乐值。

思路:
容易发现序列的欢乐值和序列的总个数以及出现次数最多的数的个数有关。
那么我们倒着做一遍,容易发现这是一个树结构,而对于直接给出的序列来说,他们是叶子结点,那么我们可以倒着标记一遍,这样可以标记出每个叶子结点出现多少次,然后暴力统计即可。

代码:

view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
struct Hash {
	int a[N];
	void init() { *a = 0; }
	void add(int x) { a[++*a] = x; }
	void gao() { sort(a + 1, a + 1 + *a); *a = unique(a + 1, a + 1 + *a) - a - 1; }
	int get(int x) { return lower_bound(a + 1, a + 1 + *a, x) - a; }
}hs;
int n, m, x[N], y[N], op[N], sze[N]; 
ll a[N], b[N];
vector <vector<int>> vec;

int main() {
	int _T; scanf("%d", &_T);
	while (_T--) {
		scanf("%d", &n);
		memset(b, 0, sizeof (b[0]) * (n + 10));
		vec.clear(); vec.resize(n + 1);
		hs.init();
		for (int i = 1; i <= n; ++i) {
			scanf("%d", op + i);
			if (op[i] == 1) {
				scanf("%d", sze + i);
				vec[i].resize(sze[i]);
				for (int j = 0; j < sze[i]; ++j) {
					scanf("%d", &vec[i][j]);
					hs.add(vec[i][j]);
				}
			} else {
				scanf("%d%d", x + i, y + i);
			}
		}
		hs.gao();
		m = hs.a[0];
		memset(a, 0, sizeof (a[0]) * (m + 10));
		b[n] = 1;
		for (int i = n; i >= 1; --i) {
			if (b[i] == 0) continue;
			if (op[i] == 1) {
				for (int j = 0; j < sze[i]; ++j) {
					vec[i][j] = hs.get(vec[i][j]);
					a[vec[i][j]] += b[i];
				}
			} else {
				b[x[i]] += b[i];
				b[y[i]] += b[i];
			}
		}
		ll tot = 0, Max = 0;
		for (int i = 1; i <= m; ++i) {
			tot += a[i];
			Max = max(Max, a[i]);
		}
		ll res = 0;
		if (Max <= tot / 2) res = tot;
	    else res = (tot - Max) * 2;
		printf("%lld\n", res);	
	}
	return 0;
}

F. Fixing Banners

签到。

代码:

view code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
char s[N];
int a[6][210]; 
vector <int> id;

bool ok() {
	char *str = "harbin";
	for (int i = 0; i < 6; ++i) {
		if (a[id[i]][str[i]] == 0)
			return false;
	}
	return true;
}

void gao() {
	do {
		if (ok()) {
			puts("Yes");
			return;
		}
	} while (next_permutation(id.begin(), id.end()));
	puts("No");
}

int main() {
	int _T; scanf("%d", &_T);
	while (_T--) {
		for (int i = 0; i < 6; ++i) {
			a[i]['h'] = 0;
			a[i]['a'] = 0;
			a[i]['r'] = 0;
			a[i]['b'] = 0;
			a[i]['i'] = 0;
			a[i]['n'] = 0;
			scanf("%s", s + 1);
			for (int j = 1, len = strlen(s + 1); j <= len; ++j) {
				a[i][s[j]] = 1;
			}
		}
		id.clear();
		for (int i = 0; i < 6; ++i) id.push_back(i);
		gao();
	}
	return 0;
}

I. Interesting Permutation

题意:
有一个排列\(a_i\),现在生成\(f, g, h\):

  • \(\forall i \in [1, n], f_i = max\{a_1, a_2, \cdots, a_i\}\)
  • \(\forall i \in [1, n], g_i = min\{a_1, a_2, \cdots, a_i\}\)
  • \(\forall i \in [1, n], h_i = f_i - g_i\)

现在给出\(h_i\),求\(a_i\)的合法方案数。

代码:

view code
#include <bits/stdc++.h>

using namespace std;

const int MOD = 1e9 + 7;

long long cntt[100000 + 10];
int x[100000 + 10];

int main() {
	int T;
	scanf("%d", &T);
	cntt[0] = 1;
	for (int i = 1; i <= 100000; ++i) cntt[i] = (cntt[i - 1] * 2) % MOD;
	while (T--) {
		int n;
		int Max, flag;
		long long ans, q = 1, t = 0, cnt;
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) scanf("%d", &x[i]);
		if (n == 1) {
			if (x[1] == 0) flag = 0;
			else flag = 1;
		}
		else { 
			if (x[1] == 0) {
				flag = 0; ans = 0; Max = x[1]; q = 1; t = 0; cnt = 0;
				for (int i = 2; i <= n; ++i) {
					if (flag == 0) {
						if (x[i] > Max) {
							++t; cnt += (x[i] - Max - 1);
							Max = x[i];
							if (Max > n - 1) flag = 1;
						}
						else if (x[i] == Max) {
							if (cnt > 0) {
								q = (q * cnt) % MOD;
								cnt--;		
							}
							else flag = 1;
						}
						else {
							flag = 1;
						}
					}
				}
			}
			else flag = 1;
		}
		//printf("f %d\n", flag);
		if (flag == 1) printf("0\n");
		else {
			ans = cntt[t] * q % MOD;
			printf("%lld\n", ans);
		}
	}
	return 0;
}

J. Justifying the Conjecture

签到。

代码:

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

int main() {
	int _T; scanf("%d", &_T);
	while (_T--) {
		int n; scanf("%d", &n);
		if (n < 6) puts("-1");
		else if (n % 2 == 0) printf("%d %d\n", 2, n - 2);
		else printf("%d %d\n", 3, n - 3);
	}
	return 0;
}

K. Keeping Rabbits

代码:

view code
#include <bits/stdc++.h>

using namespace std;

using db = double;

const int N = 1e5 + 10;

int n, k;
db w[N];

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d %d", &n, &k);
		db sum = 0;
		for (int i = 1; i <= n; ++i) {
			scanf("%lf", w + i);
			sum += w[i];
		}
		for (int i = 1; i <= n; ++i) {
			w[i] += k * w[i] / sum;
		}
		for (int i = 1; i <= n; ++i) {
			printf("%.10f%c", w[i], " \n"[i == n]);
		}
	}
	return 0;
}

L. LRU Algorithm

题意:
给出一个长度为\(n\)的操作序列,有\(q\)次询问,每次询问给出一个容量为\(m\)的cache,里面有一些元素,问这个操作序列使用\(LRU\)算法,并且cache容量在\(m\)的操作的过程中,
是否有一步这个cache里面的元素和询问给出的是一致的。

思路:
预处理出所有询问的link-list的哈希值,然后暴力模拟LRU算法执行流程。
每做一遍都处理出容量为\(i\)的cache当前的link-list哈希值,然后暴力枚举\(q\)个询问进行判断。
不取模\(Hash\)比取模要快

代码:

view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
#define dbg(x...) do { cout << "\033[32;1m" << #x << " -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template <class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << " "; err(args...); }
const int N = 5e3 + 10;
int n, m, q, a[N], used[N], b[N], len[N], ans[N]; 
ull f[N], g[N], seed = 13331; 
struct Hash {
	constexpr static ull seed = 13331;
	ull a[N];
	void clear() { a[0] = 0; }
	void add(int x, int y) { a[x] = a[x - 1] * seed + y; }
	ull get(int x) { return a[x]; }
}hs;

int main() {
	int _T; scanf("%d", &_T);
	while (_T--) {
		scanf("%d%d", &n, &q);
		for (int i = 1; i <= n; ++i) scanf("%d", a + i), used[i] = 0; 
		for (int i = 1; i <= q; ++i) {
			scanf("%d", len + i);
			hs.clear();
			for (int j = 1; j <= len[i]; ++j) scanf("%d", b + j), hs.add(j, b[j]);
			g[i] = hs.get(len[i]);
			ans[i] = 0;
			if (!g[i]) ans[i] = 1;
		}
		m = 0;
	    for (int i = 1; i <= n; ++i) b[i] = 0;	
		for (int i = 1; i <= n; ++i) {
			int x = a[i];
			if (used[x] == 0) {
				b[++m] = x;
				used[x] = m;	
			} else {
				for (int j = used[x]; j <= m; ++j) {
					b[j] = b[j + 1];
					used[b[j]] = j;
				}
				b[m] = x;
				used[x] = m;
			}
			hs.clear();
			int k = 0;
			for (int j = m; j >= 1; --j) {
				++k;
				hs.add(k, b[j]);
				f[k] = hs.get(k);
			}
			for (int j = m + 1; j <= n; ++j) {
				++k;
				hs.add(k, 0);
				f[k] = hs.get(k);
			}
			for (int j = 1; j <= q; ++j) {
				if (f[len[j]] == g[j]) 
					ans[j] = 1;
			}
		}
		for (int i = 1; i <= q; ++i)
			puts(ans[i] ? "Yes" : "No");
	}
	return 0;
}
posted @ 2019-11-04 08:26  Dup4  阅读(463)  评论(0编辑  收藏  举报