2023年ICPC杭州站题解

比赛链接:The 2023 ICPC Asia Hangzhou Regional Contest (The 2nd Universal Cup. Stage 22: Hangzhou)

D. Operator Precedence(构造)

介于一乘起来就没完没了了,所以尽可能不让乘法里出现大于等于2的数,于是可以想到构造一个类似于:x 2 -1 2 -1 … 2 -1 y

然后让 y 为 1,求 x

(自己这次还是没能想出来,感谢队友)

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n;
	cin >> n;
	cout << 2 * n - 3 << ' ' << 2 << ' ';
	for (int i = 0; i < (2 * n - 4); i ++ )
	{
		if (i % 2 != 0) cout << 2 << ' ';
		else cout << -1 << ' ';
	}
	cout << -1 << ' ' << 1 << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	cin >> t;
	while (t--)
	{
		solve();
	}
}

G. Snake Move(最短路)

标记一下蛇身在的位置需要多少次之后才能访问(用 flag 标记),其他就正常跑最短路,然后距离在 dd + 1flag[nx][ny] + 1 里取 max 即可

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};

void solve()
{
	int n, m, k;
	cin >> n >> m >> k;
	vector<vector<char>> g(n + 2, vector<char>(m + 2));
	vector<vector<int>> flag(n + 2, vector<int>(m + 2));
	vector<vector<bool>> st(n + 2, vector<bool>(m + 2));
	vector<vector<int>> dist(n + 2, vector<int>(m + 2, INF));
	priority_queue<PIII, vector<PIII>, greater<PIII>> pq;
	int px, py;
	for (int i = 1; i <= k; i ++ )
	{
		int x, y;
		cin >> x >> y;
		flag[x][y] = k - i;
		if (i == 1)
		{
			px = x, py = y;
			dist[x][y] = 0;
			pq.push({0, {x, y}});
		}
	}
	for (int i = 1; i <= n; i ++ )
		for (int j = 1; j <= m; j ++ )
			cin >> g[i][j];
	while (pq.size())
	{
		auto t = pq.top();
		pq.pop();

		int dd = t.first, x = t.second.first, y = t.second.second;
		if (st[x][y]) continue;
		st[x][y] = true;

		for (int i = 0; i < 4; i ++ )
		{
			int nx = x + dx[i], ny = y + dy[i];
			if (nx <= 0 || nx > n || ny <= 0 || ny > m) continue;
			if (st[nx][ny] || g[nx][ny] == '#') continue;
			if (dist[nx][ny] > max(dd + 1, flag[nx][ny] + 1))
			{
				dist[nx][ny] = max(dd + 1, flag[nx][ny] + 1);
				pq.push({dist[nx][ny], {nx, ny}});
			}
		}
	}

	unsigned long long ans = 0;
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= m; j ++ )
		{
			if (dist[i][j] == INF) continue;
			ans = ans + dist[i][j] * dist[i][j];
		}
	}
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

H. Sugar Sweet II(基环树+概率)

首先我们可以根据题目中给的关系建一个基环森林(也就是 b[i]i 建边,因为 b[i] 的状态会影响 i 要不要加 w[i]

然后我们把所有的点分成三类:

  • a[i] < a[b[i]] 此时 b[i] 不管加不加 w[b[i]]a[i] 都是要加 w[i] 的,将 dist[i] 标记为 1
  • a[i] >= a[b[i]] + w[b[i]] 此时 b[i] 不管加不加 w[b[i]]a[i] 都是不加 w[i] 的,将 dist[i] 标记为 0
  • a[b[i]] + w[b[i]] < a[i] < a[b[i]] 此时 b[i]i 就加,b[i] 不加 i 就不加,因为只有这里是不确定的,所以只在这种情况建边

先利用 dfs 算出每个第一类点到第三类点的距离,如果第一类点在随机排序中达到了现在我们建的图这种情况,那么第三类点就要加,那有多大的概率能达到呢?第一类点到第三类点的距离我们在 dfs 中预处理过了,所以就是全排列分之一,即 1 ( d i s t [ i ] ! ) \frac{1}{(dist[i]!)} (dist[i]!)1

之后利用乘法逆元计算答案

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e5 + 10;
const int maxn = 5e6 + 10;
const int mod = 1e9 + 7;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

int Jc[maxn];

void calJc()	//求 maxn 以内的数的阶乘 不知道开多少就1e6吧爆不了
{
    Jc[0] = Jc[1] = 1;
    for(int i = 2; i < maxn; i++) Jc[i] = Jc[i - 1] * i % mod;
}

int pow(int a, int n, int p) // 快速幂取模
{
    int ans = 1;
    while (n)
    {
        if (n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}

int niYuan(int a, int b)	//费马小定理求逆元
{
    return pow(a, b - 2, b);
}

void solve()
{
	int n;
	cin >> n;
	vector<int> a(n + 1), b(n + 1), w(n + 1);
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	for (int i = 1; i <= n; i ++ ) cin >> b[i];
	for (int i = 1; i <= n; i ++ ) cin >> w[i];
	vector<vector<int>> g(n + 1);
	vector<int> dist(n + 1);
	for (int i = 1; i <= n; i ++ )
	{
		if (a[b[i]] > a[i]) dist[i] = 1;
		else if (a[b[i]] + w[b[i]] <= a[i]) dist[0] = 0;
		else g[b[i]].push_back(i);
	}
	function<void(int)> dfs = [&](int u)
	{
		for (int i = 0; i < g[u].size(); i ++ )
		{
			int j = g[u][i];
			dist[j] = dist[u] + 1;
			dfs(j);
		}
	};
	for (int i = 1; i <= n; i ++ )
	{
		if (dist[i] == 1) dfs(i);
	}
	vector<int> ans(n + 1);
	for (int i = 1; i <= n; i ++ )
	{
		if (dist[i] == 0) ans[i] = a[i];
		else ans[i] = (a[i] + w[i] * niYuan(Jc[dist[i]], mod) % mod) % mod;
		cout << ans[i] << ' ';
	}
	cout << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	calJc();
	int t = 1;
	cin >> t;
	while (t--)
	{
		solve();
	}
}

J. Mysterious Tree(图论交互)

首先关注操作次数 ⌈ n 2 ⌉ + 3 \lceil \frac{n}{2} \rceil + 3 2n+3 ,看到这就很容易想到那个 n/2 是两两之间连一次吧,如果从头到尾都没碰到相连的边,说明不可能是星形(因为根本没有中间的那个点),一定是链,一旦碰到一个相连的边,就立刻停下

假设这条边的两个端点是 i j,如果 i j 不是 1 2 的话,直接先让 i 和 i - 2 连,有边再让 i 和 i - 1 连,如果还是有边就说明是星形,如果没有边就让 j 和 i - 2、i - 1 连(很容易判断就不详细说了),如果 i j 是 1 2 的话就需要单独注意一下

另外 n 是奇数的时候也要单独注意一下

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

int print(int a, int b)
{
	cout << "? " << a << ' ' << b << endl;
	int x; cin >> x;
	return x;
}

void solve()
{
	int n;
	cin >> n;
	int pos = -1;
	for (int i = 1; i + 1 <= n; i += 2)
	{
		int x = print(i, i + 1);
		if (x == 1)
		{
			pos = i;
			break;
		}
	}
	if (pos == -1 && n & 1)
	{
		int x = print(n - 1, n);
		if (x == 1) pos = n - 1;
	}
	if (pos == -1)
	{
		cout << "! 1" << endl;
		return;
	}
	if (pos != 1)
	{
		int x = print(pos, pos - 2);
		if (x == 1)
		{
			int y = print(pos, pos - 1);
			if (y == 1)
			{
				cout << "! 2" << endl;
				return;
			}
			else
			{
				cout << "! 1" << endl;
				return;
			}
		}
		else
		{
			int y = print(pos + 1, pos - 2);
			if (y == 0)
			{
				cout << "! 1" << endl;
				return;
			}
			else
			{
				int z = print(pos + 1, pos - 1);
				if (z == 1)
				{
					cout << "! 2" << endl;
					return;
				}
				else
				{
					cout << "! 1" << endl;
					return;
				}
			}
		}
	}
	else
	{
		int x = print(1, 3);
		if (x == 1)
		{
			int y = print(1, 4);
			if (y == 1)
			{
				cout << "! 2" << endl;
				return;
			}
			else
			{
				cout << "! 1" << endl;
				return;
			}
		}
		else
		{
			int y = print(2, 3);
			if (y == 0)
			{
				cout << "! 1" << endl;
				return;
			}
			else
			{
				int z = print(2, 4);
				if (z == 1)
				{
					cout << "! 2" << endl;
					return;
				}
				else
				{
					cout << "! 1" << endl;
					return;
				}
			}
		}
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	cin >> t;
	while (t--)
	{
		solve();
	}
}

M. V-Diagram(思维)

首先我们一定要选中间的三个数,然后我们只要再选一个比当前数大的数,整体平均值就一定会上升,(设中间最小坐标是pos)所以答案只可能是 [1, pos + 1] [pos - 1, n] [pos - 1, pos +1] [1, n],判断一下取最大即可

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n;
	cin >> n;
	vector<int> a(n + 1);
	int pos = 0;
	for (int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
		if (i != 1 && a[i] > a[i - 1] && pos == 0) pos = i - 1;
	}
	int sum1 = 0, sum2 = 0;
	for (int i = 1; i < pos - 1; i ++ ) sum1 += a[i];
	for (int i = pos + 2; i <= n; i ++ ) sum2 += a[i];
	double ans = a[pos] + a[pos - 1] + a[pos + 1];
	ans = max({1.0 * ans / 3, 1.0 * (ans + sum1) / (pos + 1), 1.0 * (ans + sum2) / (n - pos + 2), 1.0 * (ans + sum2 + sum1) / n});
	printf("%.10lf\n", ans);
}

signed main()
{
	// ios::sync_with_stdio(false);
	// cin.tie(0), cout.tie(0);

	int t = 1;
	cin >> t;
	while (t--)
	{
		solve();
	}
}
  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值