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;
}