SDNU&&QLU_ACM_ICPC_2022_Weekly_Practice_4rd

好像是2018年的宁夏邀请赛?

C. Caesar Cipher(字符串)

给出两个字符串,他们之间的关系是相同位置的字母之间有个位置移动的关系,根据给出的关系解码密文。

思路:直接计算给出的两个字符串之间的偏移关系,根据这个关系解码密文,注意对26取模,也要注意负数问题。

AC Code:

#include <bits/stdc++.h>

template <typename T>
inline void read(T &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0', ch = getchar();
	}
	x *= f;
}

template <typename T>
void write(T x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		print(x / 10);
	putchar(x % 10 + '0');
}

#define INF 0x3f3f3f3f
typedef long long ll;
const double PI = acos(-1);
const double eps = 1e-6;
const int mod = 1e9 + 7;
const int N = 1e6 + 5;
int t, n, m;
std::string s1, s2, s;

int main() {
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	while (std::cin >> t) {
		for (int i = 1; i <= t; i++) {
			std::cin >> n >> m;
			std::cin >> s1 >> s2;
			std::cin >> s;
			int cnt;
			cnt = s2[0] - 'A' - (s1[0] - 'A') ;
			if (cnt < 0)
				cnt += 26;
			for (int j = 0; j < m; j++)
				s[j] = (s[j] - 'A' - cnt + 26) % 26 + 'A';
			std::cout << "Case #" << i << ": ";
			std::cout << s << '\n';
		}
	}
	return 0;
}

B. Rolling The Polygon(数学,几何)

一个多边形内有一个点,若令这个多边形每条边沿x轴转动,问这个点经过的轨迹长度。

思路: 连接该点与每个顶点,每次转动的轨迹都是绕某一顶点旋转成的扇形弧长。因为给出的顶点是按照顺序的,可以直接遍历,以每一个点为顶点,用余弦定理计算该点的内角大小,旋转的扇形角度就是外角大小,成点到顶点的距离就是扇形弧长,计算求和。怎样遍历第一个点和最后一个?令e[0]=e[n],e[n+1]=e[1]即可。

AC Code:

#include <bits/stdc++.h>

template <typename T>
inline void read(T &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0', ch = getchar();
	}
	x *= f;
}

template <typename T>
void write(T x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		print(x / 10);
	putchar(x % 10 + '0');
}

#define INF 0x3f3f3f3f
typedef long long ll;
const double PI = acos(-1);
const double eps = 1e-6;
const int mod = 1e9 + 7;
const int N = 1e6 + 5;
int t, n, a, b;
std::string s1, s2, s;

struct node {
	double x, y;
} e[N];

double len(node x, node y) {
	return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}

double cal(node a, node b, node c, node d) {
	double A = len(a, b);
	double B = len(b, c);
	double C = len(a, c);
	double ang = (A * A + B * B - C * C) / (2 * A * B);

	ang = PI - acos(ang);
	return ang * len(b, d);
}

int main() {
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
	while (std::cin >> t) {
		for (int i = 1; i <= t; i++) {
			std::cin >> n;
			for (int j = 1; j <= n; j++) {
				std::cin >> e[j].x >> e[j].y;
			}
			node ss;
			std::cin >> ss.x >> ss.y;
			e[0].x = e[n].x;
			e[0].y = e[n].y;
			e[n + 1].x = e[1].x;
			e[n + 1].y = e[1].y;
			double ans = 0;
			for (int i = 1; i <= n; i++) {
				ans += cal(e[i - 1], e[i], e[i + 1], ss);
			}
			std::cout << "Case #" << i << ": ";
			printf("%.3lf\n", ans);
		}
	}
	return 0;
}

os:想法大概是队友提出的,,,我只是个无情的敲代码机器hhhh

D. Take Your Seat(概率)

第一次坐飞机时,这个人是1号乘客但是他丢了机票,所以他上飞机后会随机选择一个座位坐下,而他后面上飞机的人若自己的座位被占了,那也会随机选择一个座位坐下,若自己的位置没被占,则会坐在自己的座位上,这一次上飞机是按照从1~n的顺序上的;

第二次坐飞机时,也有一个人丢了机票,人们上飞机后的行为与第一次一样,但是这次乘客上飞机的顺序是随机的;

问这两次最后一个上飞机的人可以坐在自己的位置上的概率。

思路: 第一种情况可以由数学归纳法得到结果:

n==1时,P(1)=1;

n==2时,第一个人上飞机随意选择一个座位,第二个人上飞机坐在自己位置上的概率P(2)=1/2;

n==3时,第一个人选择正确的座位概率P=1/3;若选择最后一个人的座位,则P=0;若选择第二个人的座位,那可以把第二个人选择位置转化成第一个人的情况,即P=1/3*1/2,综上所述,此时P(3)=1/3+0+1/3*P(2)=1/2;

n==4时,第一个人选择正确的位置P=1/4;若选择最后一个人的座位,则P=0;若选择第二个人的座位,则P=P(3)*1/4;若选择第三个人的位置,则P=P(2)*1/4;综上所述,此时P(4)=1/4+0+P(3)*1/4+P(2)*1/4=1/2;

根据数学归纳法,可以得到n等于任意值k时,概率均为1/2。

第二种情况可以这样考虑:

若丢了机票的人最后一个上飞机,那样每个人的座位都是正确的,则P=1/m,这个概率是丢了机票的人最后一个上飞机的概率;

若他是第i个上飞机的,则他之前的人座位均是正确的,从第i个人开始,上飞机的情况和概率与第一种情况相同,这样的上飞机顺序概率为1-1/m,则P=1/2*(1-1/m);

综上所述,P=1/m+1/2*(1-1/m)=(m+1)/(2*m)。

最后注意两种情况的特判,即人数为1时,P=1.

AC Code:

#include <bits/stdc++.h>
#pragma GCC optimize(2)

template <typename T>
inline void read(T &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0', ch = getchar();
	}
	x *= f;
}

template <typename T>
void write(T x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		print(x / 10);
	putchar(x % 10 + '0');
}

#define INF 0x3f3f3f3f
typedef long long ll;
const double PI = acos(-1);
const double eps = 1e-6;
const int mod = 1e9 + 7;
const int N = 1e6 + 5;
int t;
double n, m;

int main() {
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
	while (~scanf("%d", &t)) {
		for (int k = 1; k <= t; k++) {
			std::cin >> n >> m;
			std::cout << "Case #" << k << ": ";
			if (n == 1)
				printf("1.000000 ");
			else
				printf("0.500000 ");
			if (m == 1)
				printf("1.000000\n");
			else {
				printf("%.6lf\n", (m + 1) / (2 * m));
			}
		}
	}
	return 0;
}

os:概率论还没有学过,但是概率计算的思想可以学习

H. Fight Against Monsters(结构体排序,贪心)

 给出n个怪兽的生命值和攻击力,一开始怪兽先攻击,hero收到的伤害是所有未死亡怪兽的攻击力之和,hero每次只能打一个怪兽且对于每一个怪兽的攻击值是递增的,第一次打某个怪兽的伤害是1,第二次打同一个怪兽的伤害是2,以此类推,问hero在全部杀死怪兽前收到最少的伤害值是多少。

思路:结构体排序,但是注意排序方式。对于每只怪兽,先处理打击次数,我们应该先打平均伤害高的怪兽,即伤害/打击次数高的排在前面。不能只看伤害高这一参数,若某一怪兽伤害不高但是生命值很高也应该被考虑进去。

AC Code:

#include <bits/stdc++.h>
#pragma GCC optimize(2)

template <typename T>
inline void read(T &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0', ch = getchar();
	}
	x *= f;
}

template <typename T>
void write(T x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		print(x / 10);
	putchar(x % 10 + '0');
}

#define INF 0x3f3f3f3f
typedef long long ll;
const double PI = acos(-1);
const double eps = 1e-6;
const int mod = 1e9 + 7;
const int N = 1e5 + 5;
int t, a[1005];
int n;

struct node {
	int hp, att, tim;
} e[N];

void init() {
	a[0] = 0;
	for (int i = 1; i <= 1000; i++) {
		a[i] = a[i - 1] + i;
	}
}

bool cmp(node a, node b) {
	if ((ll)a.att * (ll)b.tim > (ll)b.att * (ll)a.tim)
		return true;
	else
		return false;
}

int main() {
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	init();
	while (std::cin >> t) {
		for (int k = 1; k <= t; k++) {
			std::cin >> n;
			for (int i = 1; i <= n; i++) {
				std::cin >> e[i].hp >> e[i].att;
				int pos = std::lower_bound(a + 1, a + 1 + 1000, e[i].hp) - a;
				if (a[pos] < e[i].hp)
					pos++;
				e[i].tim = pos;
			}
			std::sort(e + 1, e + 1 + n, cmp);
			ll ans = 0, sum = 0;
			for (int i = 1; i <= n; i++) {
				sum += e[i].att;
			}
			for (int i = 1; i <= n; i++) {
				ans += (ll)e[i].tim * sum;
				sum -= e[i].att;
			}
			std::cout << "Case #" << k << ": ";
			std::cout << ans << '\n';
		}
	}
	return 0;
}

A. Maximum Element In A Stack(模拟栈)

 去原题摘了中间那一段代码,,,Gym里支离破碎的:

模拟堆栈,已经给出的rng61()函数直接复制即可,主函数就是给的gen(),我们需要模拟的就是满足gen()函数中的if,则将元素push进栈中,若不满足,则pop出栈,每次进入栈后,令最大值为栈顶,对于栈顶进行异或和乘法操作。

思路:复制原题目的代码,直接用数组模拟栈即可,注意每次进栈之后要比较与栈内数的大小关系,确定新的栈顶。

AC Code:

#include <bits/stdc++.h>
#pragma GCC optimize(2)

template <typename T>
inline void read(T &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0', ch = getchar();
	}
	x *= f;
}

template <typename T>
void write(T x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		print(x / 10);
	putchar(x % 10 + '0');
}

#define INF 0x3f3f3f3f
typedef long long ll;
const double PI = acos(-1);
const double eps = 1e-6;
const int mod = 1e9 + 7;
const int N = 5e6 + 5;
int n, t, m, p, q;
unsigned int SA, SB, SC;
int cnt[N], cc[N];
ll s[N];

unsigned int rng61() {
	SA ^= SA << 16;
	SA ^= SA >> 5;
	SA ^= SA << 1;
	unsigned int t = SA;
	SA = SB;
	SB = SC;
	SC ^= t ^ SA;
	return SC;
}

int main() {
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin >> t;
	for (int k = 1; k <= t; k++) {
		std::cin >> n >> p >> q >> m >> SA >> SB >> SC;
		ll ans = 0;
		int top = 0;
		for (int i = 1; i <= n; i++) {
			if (rng61() % (p + q) < p) {
				ll res = rng61() % m + 1;
				s[++top] = res;
				s[top] = std::max(s[top], s[top - 1]);
			} else {
				top = std::max(0, top - 1);
			}
			ans ^= ((ll)s[top] * (ll)i);
		}
		std::cout << "Case #" << k << ": ";
		std::cout << ans << '\n';
	}
	return 0;
}

os:这个题非常不应该,,,俩人看了半天题没看懂,结果一看,嗐......

F. Moving On

每个城市都有一个被抢劫的概率,给出城市与城市之间距离的邻接矩阵,有q个询问,问从u到v所经过的城市的被抢劫概率都小于w的最短路。

思路: n的范围较小,我们可以用floyd,但是裸的floyd肯定TLE,所以需要对于这个被抢劫概率进行一个处理,需要对于每个点的点权进行排序,floyd对于每个w值不同的断点进行记录,这样预处理即可,就是O(N^3)的复杂度,可以通过。采用三维数组存从i到j经过最大w的位置为k的最短路径,需要数组的灵活运用。

AC Code:

#include <bits/stdc++.h>

template <typename T>
inline void read(T &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0', ch = getchar();
	}
	x *= f;
}

template <typename T>
void write(T x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		print(x / 10);
	putchar(x % 10 + '0');
}

#define INF 0x3f3f3f3f
typedef long long ll;
const double PI = acos(-1);
const double eps = 1e-6;
const int mod = 1e9 + 7;
const int N = 200 + 5;
int i, j, k, n, m, t, q;
int f[N][N][N];
int r[N];
int id[N];

bool cmp(int a, int b) {
	return r[a] < r[b];
}

int main() {
	std::cin >> t;
	for (int l = 1; l <= t; l++) {
		std::cin >> n >> q;
		for (i = 1; i <= n; i++) {
			id[i] = i;
			std::cin >> r[i];
		}
		std::sort(id + 1, id + 1 + n, cmp);
		for (i = 1; i <= n; i++) {
			for (j = 1; j <= n; j++) {
				std::cin >> f[i][j][0];
			}
		}
		for (k = 1; k <= n; k++) {
			for (i = 1; i <= n; i++) {
				for (j = 1; j <= n; j++) {
					f[i][j][k] = std::min(f[i][j][k - 1], f[i][id[k]][k - 1] + f[id[k]][j][k - 1]);
				}
			}
		}
		printf("Case #%d:\n", l);
		while (q--) {
			int u, v, w;
			std::cin >> u >> v >> w;
			int ans = 0;
			for (i = 1; i <= n; i++) {
				if (r[id[i]] <= w)
					ans = i;
			}
			std::cout << f[u][v][ans] << '\n';
		}
	}
	return 0;
}

我也想继续补下面的题,,但是树状DP,状压什么的还没学,,,我滚了wwww

若有错误请指教orzorz

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值