The Preliminary Contest for ICPC Asia Shenyang 2019 题解

The Preliminary Contest for ICPC Asia Shenyang 2019

【B. Dudu’s maze】

【题目大意】
给定一个无向图G = (n, m),图中有k个陷阱,其余节点每个节点有一个糖果,此外,你有一次从陷阱节点跳跃与其相邻的一个随机节点的机会,问从1出发获得的糖果数量的期望是多少

【解题思路】
首先从1出发bfs,易知与节点1相连的糖果房取到糖果的概率为1,然后枚举到达与1相连的陷阱房使用跳跃功能后所获得的糖果的最大数量,该概率为所获得的糖果除以陷阱节点度数(跳到任一节点的概率相同),加上之前的就是答案

枚举是可以用dfs或者bfs,然后可以考虑用并查集进行优化

【AC代码】

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define max(a, b) ((a) > (b) ? (a) :(b))
#define min(a, b) ((a) < (b) ? (a) :(b))
#define _Rep(i, n) for(register int i = 1; i < (n); ++i)
#define Rep(i, n) for(register int i = 1; i <= (n); ++i)
#define _rep(i, n) for(register int i = 0; i < (n); ++i)
#define rep(i, n) for(register int i = 0; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int maxn = 100010;
const int maxm = 200010;
const int INF = 0x3f3f3f3f;
int n, m, k;
vector<int> g;  //记录与1相连的陷阱
vector<int> G[maxn];  //邻接矩阵存图
int d[maxn];  //节点度数
int pre[maxn];  //并查集优化
int nums[maxn];  //记录节点i所能获得的最大糖果
bool vis[maxn];  //标记1能够到达的节点
bool check[maxn];  //标记陷阱节点
bool judge[maxn];  //标记是否已经dfs
inline int read() {
	register int x = 0, w = 0; register char ch = 0;
	while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return w ? -x : x;
}
inline void write(register int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
inline int find(register int x) {
	return pre[x] == x ? x : pre[x] = find(pre[x]);
}
inline void join(register int x, register int y) {
	register int fx = find(x);
	register int fy = find(y);
	if (fx == fy) return;
	pre[fx] = fy;
	nums[fy] += nums[fx];
}
inline double bfs(register int s) {
	register queue<int> que;
	que.push(s);
	vis[s] = true;
	register int res = 1;
	while (!que.empty()) {
		register int u = que.front();
		que.pop();
		register int l = G[u].size();
		_rep(i, l) {
			register int v = G[u][i];
			if (vis[v]) continue;
			if (!check[v]) {
				que.push(v);
				vis[v] = true;
				++res;
			}
			else {
				g.push_back(v);
				vis[v] = true;
			}
		}
	}
	return double(res * 1.0);
}
inline void dfs(register int u) {
	judge[u] = true;
	int l = G[u].size();
	_rep(i, l) {
		register int v = G[u][i];
		if (vis[v] || check[v] || judge[v]) continue;  //与1联通或者也是陷阱或者已经访问
		dfs(v);
		join(u, v);
	}
}
inline void init() {
	rep(i, n) {
		G[i].clear();
		nums[i] = 1;
		pre[i] = i;
		d[i] = vis[i] = check[i] = judge[i] = 0;
	}
	g.clear();
}
int main() {
	register int t;
	t = read();
	while (t--) {
		n = read(), m = read(), k = read();
		init();
		Rep(i, m) {
			register int x, y;
			x = read(), y = read();
			G[x].push_back(y);
			G[y].push_back(x);
			++d[x], ++d[y];
		}
		Rep(i, k) {
			register int x;
			x = read();
			check[x] = true;
		}
		register double ans = bfs(1);
		register double Max = 0.0;
		int len = g.size();
		_rep(i, len) {
			register int u = g[i];
			register double temp = 0.0;
			register int l = G[u].size();
			_rep(j, l) {
				register int v = G[u][j];
				if (vis[v] || check[v]) continue;  //与1联通或者也是陷阱
				dfs(v);
				temp += nums[find(v)];
			}
			temp = temp / (double)(d[u] * 1.0);
			Max = max(Max, temp);
		}
		ans += Max;
		printf("%.8f\n", ans);
	}
	return 0;
}

【C. Dawn-K’s water】

【题目大意】
有n种物品,每种物品有价格和重量,问最少要花多少钱才能买到总重量大于m,问花的钱和物品的总重

【解题思路】
典型的完全背包,跑一次完全背包,将体积看成1e6,从m往后遍历,找到价格最小的即为答案

【AC代码】

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define max(a, b) ((a) > (b) ? (a) :(b))
#define min(a, b) ((a) < (b) ? (a) :(b))
#define _Rep(i, n) for(register int i = 1; i < (n); ++i)
#define Rep(i, n) for(register int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;
const ll INF = 0x3f3f3f3f;
struct Water {
	int p, c;
}a[1010];
int dp[10010];
inline int read() {
	register int x = 0, w = 0; register char ch = 0;
	while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return w ? -x : x;
}
inline void write(register int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
int main() {
	register int n, m;
	while (~scanf("%d%d", &n, &m)) {
		Rep(i, n) {
			a[i].p = read(), a[i].c = read();
		}
		memset(dp, INF, sizeof(dp));
		dp[0] = 0;
		Rep(i, n) {
			for (register int j = a[i].c; j <= 10000; ++j) {
				dp[j] = min(dp[j], dp[j - a[i].c] + a[i].p);
			}
		}
		register int pri = dp[m];
		int vol = m;
		for (int i = m + 1; i <= 10000; ++i) {
			if (dp[i] <= pri) {
				pri = dp[i];
				vol = i;
			}
		}
		write(pri), putchar(' '), write(vol), puts("");
	}
	return 0;
}

【E. Gugugu’s upgrade schemes】

【题目大意】
有n种物品,每两件不同的物品可以合成一件新的物品,问有多少种合成方法(也可以不合成),答案对p取模,p保证是质数

【解题思路】
很容易想到这是一个集合的划分种数问题,即贝尔数,考虑同余方程
在这里插入图片描述
虽然n很大,但是p只有1000,可以先把p以内的贝尔数预处理出来,然后递归一下,很快就可以跑出来

【AC代码】

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define max(a, b) ((a) > (b) ? (a) :(b))
#define min(a, b) ((a) < (b) ? (a) :(b))
#define _Rep(i, n) for(register int i = 1; i < (n); ++i)
#define Rep(i, n) for(register int i = 1; i <= (n); ++i)
#define _rep(i, n) for(register int i = 0; i < (n); ++i)
#define rep(i, n) for(register int i = 0; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int maxn = 1000010;
const int maxm = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
int n, p;
int num[1010][1010];
int ans[maxn];
inline int read() {
	register int x = 0, w = 0; register char ch = 0;
	while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return w ? -x : x;
}
inline void write(register int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
inline void pre() {  //预处理
	memset(num, 0, sizeof(num));
	memset(ans, 0, sizeof(ans));
	num[1][1] = 1;
	for (register int i = 2; i <= p; ++i) {
		Rep(j, i) {
			num[i][j] = (num[i - 1][j - 1] + j * num[i - 1][j] % p) % p;
		}
	}
	ans[0] = ans[1] = 1;
	for (int i = 2; i <= p; ++i) {
		for (int j = 1; j <= i; ++j) {
			ans[i] = (ans[i] + num[i][j]) % p;
		}
	}
}
inline int dfs(register int x) {  //递归求贝尔数
	if (x < 0) return 0;
	if (ans[x]) return ans[x];
	return ans[x] = (dfs(x - p) + dfs(x - p + 1)) % p;
}
int main() {
	register int t;
	t = read();
	while (t--) {
		n = read(), p = read();
		pre();
		write(dfs(n));
		puts("");
	}
	return 0;
}

【F. Honk’s pool】

【题目大意】
有n个水池,每天向最少的池子里倒一升水,将最多的池子取一升水,问m天后水最多的池子和水最少的池子的差值为多少

【解题思路】
稍加模拟就会发现如果天数够多,那么最后一定会达到一个平衡态,首先特判平衡态,然后优先队列模拟就好

【AC代码】

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define max(a, b) ((a) > (b) ? (a) :(b))
#define min(a, b) ((a) < (b) ? (a) :(b))
#define _Rep(i, n) for(register int i = 1; i < (n); ++i)
#define Rep(i, n) for(register int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;
const ll INF = 0x3f3f3f3f;
ll a[maxn];
inline ll read() {
	register ll x = 0, w = 0; register char ch = 0;
	while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return w ? -x : x;
}
inline void write(register int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
int main() {
	register ll n, m;
	while (~scanf("%lld%lld", &n, &m)) {
		register priority_queue<ll> que;
		register priority_queue<ll, vector<ll>, greater<ll> > Que;
		register ll sum = 0;
		Rep(i, n) {
			a[i] = read();
			que.push(a[i]);
			Que.push(a[i]);
			sum += a[i];
		}
		register ll temp = sum / n;
		bool flag = sum % n ? true : false;  //平衡态
		register ll delta = 0;
		Rep(i, n) {
			if (a[i] == temp || (flag && a[i] == temp + 1)) continue;  //已经为平衡态的池子不用处理
			delta += abs(temp - a[i]);
		}
		if (m >= delta / 2) {  //天数大于达到平衡态的天数
			flag ? puts("1") : puts("0");
			continue;
		}
		while (m--) {  //否则优先队列模拟
			register ll u = que.top();
			que.pop();
			register ll U = Que.top();
			Que.pop();
			--u, ++U;
			que.push(u);
			Que.push(U);
		}
		write(que.top() - Que.top());
		puts("");
	}
	return 0;
}

【G. Special necklace】

【题目大意】
有n个三角形,总电阻为2a,有n根棍,电阻为a,现在将他们,连接如下:
在这里插入图片描述
电流从b流向a,问总电阻为多少

【解题思路】
简单物理,注意细节
首先要读懂题目,是三角形总电阻为2a,根据等价电路图可以求出来每条边的电阻为3a
在这里插入图片描述
然后考虑有一个每一个三角形和一根棍,长这个样子
在这里插入图片描述
算出来总电阻为5 * a / 3

当有很多个三角形和棍子并联在一起的时候,可以将电路图画成这个样子

在这里插入图片描述
从下至上依次标号为1,2,…,n,那么它的迭代式即为

在这里插入图片描述
由公式可以知道,随着并联次数的增多,Rn-1会趋近于无穷,那么Rn就会趋近于R1 = 5 * a / 3

事实上打表发现当n达到10的时候答案己经不会改变了,那么将上式化简一下,用递归或者递推求出答案即可,时间接近于O(1)

【AC代码】

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define max(a, b) ((a) > (b) ? (a) :(b))
#define min(a, b) ((a) < (b) ? (a) :(b))
#define _Rep(i, n) for(register int i = 1; i < (n); ++i)
#define Rep(i, n) for(register int i = 1; i <= (n); ++i)
#define _rep(i, n) for(register int i = 0; i < (n); ++i)
#define rep(i, n) for(register int i = 0; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int maxn = 100010;
const int maxm = 3e5 + 10;
const ll INF = 0x3f3f3f3f;
string s;
int n;
double a;
inline int read() {
	register int x = 0, w = 0; register char ch = 0;
	while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return w ? -x : x;
}
inline void write(register int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
inline double solve(int x) {
	if (x == 1) {
		return 5.0 * a / 3.0;
	}
	double Ri = solve(x - 1);
	double temp = 5.0 * Ri + 3.0 * a;
	double res = (3.0 * a * temp) / (2.0 * temp - Ri);
	return res;
}
int main() {
	register int t;
	t = read();
	while (t--) {
		cin >> s;
		scanf("%lf", &a);
		n = s.size() > 1 ? 10 : atoi(s.c_str());  //大于10直接等于10
		printf("%.10f\n", solve(n));
	}
	return 0;
}

【J. Ghh Matin】

【题目大意】
给定一个有n个节点的图,每个节点的入度和出度相同,问你得到满足从任一节点出发经过不超过x条边就可以回到起点的图的概率是多少,输出概率mod(1e9),即(a / b) mod p = (a * inv(b)) mod p

【解题思路】
参考https://www.cnblogs.com/tongseli/p/11730445.html

【AC代码】

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define max(a, b) ((a) > (b) ? (a) :(b))
#define min(a, b) ((a) < (b) ? (a) :(b))
#define _Rep(i, n) for(register int i = 1; i < (n); ++i)
#define Rep(i, n) for(register int i = 1; i <= (n); ++i)
#define _rep(i, n) for(register int i = 0; i < (n); ++i)
#define rep(i, n) for(register int i = 0; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int maxm = 3e5 + 10;
const ll INF = 0x3f3f3f3f;
ll sum[maxn];
inline ll read() {
	register ll x = 0, w = 0; register char ch = 0;
	while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return w ? -x : x;
}
inline void write(register ll x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
inline ll QuickPower(register ll a, register ll b) {
	ll res = 1;
	while (b) {
		if (b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
inline ll inv(register int x) {
	return QuickPower(x, mod - 2);
}
inline void pre() {
	_Rep(i, maxn) {
		sum[i] = (sum[i - 1] + inv(i)) % mod;
	}
}
int main() {
	register int t = read();
	pre();
	while (t--) {
		register ll n = read(), x = read();
		if (x >= n) puts("1");
		else {
			ll ans = ((mod + 1) - (sum[n] - sum[x])) % mod;
			write(ans), puts("");
		}
	}
	return 0;
}

【K. Guanguan’s Happy water】

【题目大意】
广广在前k天会存并且喝冰箱里的可乐,题目给定前k天中冰箱的可乐数目,以及k+1至2k天中广广希望冰箱中的可乐数目,给定n,让你求出1-n天冰箱中总可乐数目之和的期望

【解题思路】
什么都没给,那k天的期望值应当是k+1至2k天的期望和,那么答案就是估计量加上已知量,即ans = sum(1, k + 2 … 2k) + (n - k) / k * sum(k + 1, k + 2 … 2k) + expected sum(k + 1, k + 2 … (n - k) % k)

注意n有可能小于k,并且这题数据很严格,必须取模保证不会溢出

【AC代码】

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define N 100
#define max(a, b) ((a) > (b) ? (a) :(b))
#define min(a, b) ((a) < (b) ? (a) :(b))
#define _Rep(i, n) for(register int i = 1; i < (n); ++i)
#define Rep(i, n) for(register int i = 1; i <= (n); ++i)
#define _rep(i, n) for(register int i = 0; i < (n); ++i)
#define rep(i, n) for(register int i = 0; i <= (n); ++i)
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int maxm = 3e5 + 10;
const ll INF = 0x3f3f3f3f;
ll a[N], b[N];
inline ll read() {
	register ll x = 0, w = 0; register char ch = 0;
	while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return w ? -x : x;
}
inline void write(register ll x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
int main() {
	register ll t = read();
	while (t--) {
		register ll k = read(), n = read();
		register ll suma = 0, sumb = 0;  //代表已知的前k天的和以及后k天的期望和
		Rep(i, k) {
			a[i] = read();
			suma = (suma + a[i]) % mod;
		}
		Rep(i, k) {
			b[i] = read();
			sumb = (sumb + b[i]) % mod;
		}
		register ll ans = 0;
		if (k >= n) {
			Rep(i, n) {
				ans = (ans + a[i]) % mod;
			}
		}  //n小于k答案就是前k天的和
		else {  //否则为suma加上期望和
			register ll nums = (n - k) / k;
			register ll mol = n - k - nums * k;
			ans = (suma + (nums % mod) * sumb % mod) % mod;  //注意这里乘法可能会溢出,需要取模
			Rep(i, mol) {
				ans = (ans + b[i]) % mod;
			}
		}
		write(ans), puts("");
	}
	return 0;
}
发布了40 篇原创文章 · 获赞 2 · 访问量 3234

猜你喜欢

转载自blog.csdn.net/weixin_44211980/article/details/103997867