Loading

7.24 个人赛

比赛链接: https://www.luogu.com.cn/contest/120468#description



A - faebdc玩扑克

解题思路

本题涉及约瑟夫环问题, 约瑟夫环就是n个人围成一个圈从1开始挨个报数, 设间隔m, 报到m这个数的那个人出局, 然后他的下一个个重新从1开始报, 直到场上只剩下一个人; 按出局的先后输出序列;
本题的间隔m为2; 并且属于是约瑟夫环的逆运算, 约瑟夫环是给定原序列, 求出局序列; 本题是给定出局序列, 求原序列; 这个地方有点绕, 认真思考一下, 以n = 13为例: 出局序列就是 1 2 3 4 5 6 7 8 9 10 11 12 13; 所求的原序列为 7 1 12 2 8 3 11 4 9 5 13 6 10; 对原序列进行约瑟夫环问题求解, 间隔m为2, 会发现结果就是 1~13;

设n= 13, 逆运算的求解思路为: 我们对原序列1 2 3 4 5 6 7 8 9 10 11 12 13以间隔m = 2求解约瑟夫环问题; 会得到出局序列为2 4 6 8 10 12 1 3 5 7 9 11 13; 注意原序列1~13并不是个的编号, 而是这个人所坐的位置; 我们对他求约瑟夫环问题是为了知道, 坐在第x个位置的人会是第几个出局的; 例如坐在第2个位置的人会是第1个出局的, 坐在第1个位置的人会是第7个出局的; 注意我们要求的出局序列是1~13; 1是第一个出局的, 所以要把编号为1的人放在第2个位置上, 同理, 要把编号为7的人放在第1个位置上; 这样我们就得到了原序列7 1 12 2 8 3 11 4 9 5 13 6 10;

神秘代码1

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10, mod = 1e9 + 7;
int n, m, k, idx;
int r[N], f[N];
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	int x = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= 2; j++) {
			x++;
			if (x > n) x = 1;
			if (r[x] != 0) j--;
		}
		r[x] = i;// 把编号为i的人放在第x个位置上
	}
	for (int i = 1; i <= n; i++) cout << r[i] << ' ';
	return 0;
}

下面这个代码思路和上面一样, 都是找到坐在第几个位置的人会是第几个出局的; 不过本代码是按照本题的题意进行模拟得到的, 上面的代码是用的约瑟夫环;

神秘代码2

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6+ 10, mod = 1e9 + 7;
int n, m;
queue<int> q; 
deque<int> v;
int r[N];
signed main() {
	scanf("%lld", &n);
	for (int i = 1; i <= n; i++) q.push(i);
	int idx = 1;
	while (q.size()) {
		int t1 = q.front();
		q.push(t1);
		q.pop();
		int t2 = q.front();
		v.push_back(t2);
		q.pop();
	}
	for (int i = 1; i <= n; i++) {
		r[v.front()] = i;
		v.pop_front();
	}
	for (int i = 1; i <= n; i++) {
		printf("%lld ", r[i]);
	}
	return 0;
}




B - PLATFORME 平板

解题思路

这个题我看n只有100就想着直接暴力; 我们把所有平板按高度从大到小排序; 对于每个木板, 我们从高到低遍历所有比它低的木板, 看有没有能够承载它的木板; 由于本题的设定, 要对边界进行特殊处理; 如果有的话距离即为高度差, 然后打个标记, 表示这个平板的这条腿不用接地; 遍历完后, 再遍历一次所有平板, 如果又平板的某条腿没有标记, 就让他接地即可, 距离即为平板高度; 最后输出距离的总和;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 500+ 10, mod = 1e9 + 7;
int n, m, k, idx;
struct stu {
	int a, b, c;
}ed[N];
bool cmp(stu a, stu b) {
	return a.a > b.a;
}
bool lt[N], rt[N];
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		ed[i] = { a,b,c };
	}
	sort(ed + 1, ed + 1 + n,cmp);
	int res = 0;
	for (int i = 1; i <= n; i++) {
		int x = ed[i].b, y = ed[i].c, z = ed[i].a;
		for (int j = i+1; j <= n; j++) {
			int l = ed[j].b, r = ed[j].c, h = ed[j].a;
			if (z <= h) continue;
			if (x >= l && x < r&&!lt[i]) {
				res = res + z - h;
				lt[i] = true;
			}
			if (y > l && y <= r&&!rt[i]) {
				res = res + z - h;
				rt[i] = true;
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		if (!lt[i]) res += ed[i].a;
		if (!rt[i]) res += ed[i].a;
	}
	cout << res;
	return 0;
}




D - Best Spot S

解题思路

根据题意很容易就读出来是一个多源最短路问题; 用Floyd算法求出最短路后以每个牧场作为起点遍历所有喜欢的牧场得到多个总距离; 最后找到最小的那个即可;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 500+ 10, mod = 1e9 + 7;
int n, m, k, idx;
vector<int> v;
int g[N][N];
void floyd() {
	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
			}
		}
	}
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (i == j) g[i][j] = g[j][i] = 0;
			else g[i][j] = g[j][i] = 1e9;
		}
	}
	for (int i = 1; i <= m; i++) {
		int x;
		cin >> x;
		v.push_back(x);
	}
	for (int i = 1; i <= k; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		g[a][b] = g[b][a] = min(g[a][b], c);
	}
	floyd();
	int minn = 1e9;
	int pos;
	for (int i = 1; i <= n; i++) {
		int res = 0;
		for (int x : v) {
			res += g[i][x];
		}
		if (res < minn) {
			pos = i;
			minn = res;
		}
	}
	cout << pos;
	return 0;
}




E - 统计单词个数

解题思路

即使看了标签知道要用dp也还是做不出来...感觉需要考虑的细节太多了, 把握不住...
前面的字符串处理不多说; sum[i][j]表示字符串中位置从i到j之间的单词数量; 这里有一个细节, i和j都是从大到小循环, 因为两个单词不能共用同一个首字母, 所以从后往前遍历时, 首字母都是新开的, 一定不会用共用的(这里的处理真的是太强了, Orz), s[j][i]先继承前面s[j+1][i]的单词数, 然后利用check函数看看有没有以第j位的字母作为首字母的单词, 如果有则+1; check函数利用了string的substr ( 子串开始的位置, 子串的长度 ) 函数用于获取子串; 用find函数获取子串中该单词首字母的位置, 如果位置为0说明有单词的首字母在第就j位, 返回真;
接下来就是dp过程, 状态表示: dp[i][j]表示截止到第i位, 且分成j块时的最多单词数; 状态计算: 设断点的位置是h, 则dp[i][j] = max( dp[i][j], dp[h][j -1] + sum[h+1][i] );
首先是初始化的过程, 也就是j=1时 dp[i][1] = sum[1][i]; 然后在状态计算时, 先枚举字符串长度i, 再枚举块数j, 最后枚举断点h; 注意j <= m的同时别忘了小于i; 断点h要从j开始, 因为字符串在断点h这个位置往前要分成j-1块, 也就是说字符串最短也要j, 而且h必须小于i, 因为h往后还有一块; 最后输出答案dp[len][m];

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 200 + 10, mod = 1e9 + 7;
int n, m, k, len;
map<string, int> mp;
string s = "0";
int sum[N][N], dp[N][N];
bool check(int l, int r) {
	string x = s.substr(l, r - l + 1);
	for (auto t : mp) {
		string a = t.first;
		if (x.find(a) == 0) return true;
	}
	return false;
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	string ch;
	string dc;
	for (int i = 1; i <= n; i++) {
		cin >> ch;
		s += ch;
	}
	cin >> k;
	for (int i = 1; i <= k; i++) {
		cin >> dc;
		mp[dc]++;
	}
	len = s.size()-1;
	for (int i = len; i >= 1; i--) {
		for (int j = i; j >= 1; j--) {
			sum[j][i] = sum[j + 1][i];
			if (check(j, i)) sum[j][i]++;
		}
	}

	for (int i = 1; i <= len; i++) dp[i][1] = sum[1][i];
	for (int i = 1; i <= len; i++) {
		for (int j = 1; j < i && j <= m; j++) {
			for (int h = j; h < i; h++) {
				dp[i][j] = max(dp[i][j], dp[h][j - 1] + sum[h + 1][i]);
			}
		}
	}
	cout << dp[len][m];
	return 0;
}




G - 无穷的序列

解题思路

签到题, 根据累加求和的公式n*(n+1)/2; 用二分查找是否存在n满足该公式; 若有则为1, 否则为0;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6+ 10, mod = 1e9 + 7;
int n, m, idx;
vector<int> v; 
int check(int x) {
	x = x * (x + 1);
	if (x < m) return 1;
	else if (x > m) return 2;
	else if (x == m) return 3;
}
signed main() {
	scanf("%lld", &n);
	while (n--) {
		scanf("%lld", &m);
		bool f = false;
		if (m == 1) printf("1\n");
		else {
			m--;
			m = m * 2;
			int l = 1, r = 1e5;
			while (l < r) {
				int mid = l + r + 1>> 1;
				if (check(mid)==1) {
					l = mid+1;
				}
				else if(check(mid) == 2) r = mid - 1;
				else {
					f = true;
					break;
				}
			}
			if (f) printf("1\n");
			else {
				int x = l * (l + 1);
				if (x == m) printf("1\n");
				else printf("0\n");
			}
		}
	}
	return 0;
}




H - 山峰暸望

解题思路

这个题的数据是不是太水了...1e8的复杂度竟然没有TLE, 本以为要用dp, 结果暴力就直接过了;
为什么还是用了暴力? 因为算法标签只有枚举和模拟, 没有dp...

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, pair<int, int>> PIP;
const int N = 1e4 + 10, mod = 1e9 + 7;
int n, m, idx;
vector<PIP> v;
int h[N];
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> h[i];
	}
	int maxn = 0;
	for (int i = 1; i <= n; i++) {
		int l = i, r = i;
		while (h[l - 1] <= h[l]&&l>1) l--;
		while (h[r + 1] <= h[r]&&r<n) r++;
		maxn = max(maxn, r - l + 1);
	}
	cout << maxn;
	return 0;
}




J - “非常男女”计划

解题思路

题意很简单, 不过我还是看了标签才知道要用前缀和...还得练啊; 不过既然知道要用前缀和, 我突然闪过一个想法, 不如把女生的0换成-1; 这样这个男女的平衡的话就前缀和就为0; 男女平衡一共有两个情况: 一是前缀和s[n]为0, 意思是从最开始到第n个人恰好是平衡的; 二是前缀和的差为0, 即s[n]-s[m]=0, 意思是从第m+1个人到第n个人是平衡的; 然后我们会发现如果要求n和m是O(n^2)的复杂度, 肯定是要优化的; 所以我们对第二种情况的优化策略为: s[n]-s[m]=0我们将其理解为是s[n]=s[m], 这样当我们遍历前缀和时, 如果s[i]=a; 那我们用map把当前的位置i存下来, 即mp[a]=i; 这样之后如果我们又遇到s[i]=a; 那么用当前的i - mp[a]就是一个男女平衡的序列长度;
至于为什么mp[a]不用更新,. 是因为i从小到大遍历, 如果要求最长的序列长度, 那mp[a]就要存最小的i, 也就是第一次s[i]=a时的i;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5+ 10, mod = 1e9 + 7;
int n, m, k, idx;
int s[N], g[N];
map<int,int> mp;
signed main() {
	int n;
	cin >> n;
	int maxn = 0;
	for (int i = 1; i <= n; i++) {
		cin >> g[i];
		if (g[i] == 0) g[i] = -1;
		s[i] = s[i - 1] + g[i];
		if (s[i] == 0) {
			maxn = max(maxn, i);
			continue;
		}
		if (mp[s[i]]) {
			maxn = max(maxn, i - mp[s[i]]);
		}
		else mp[s[i]] = i;
	}
	cout << maxn;
	return 0;
}




K - Milk Scheduling S

解题思路

根据题意可以发现是一个关于拓扑排序的题; 而难点在于挤奶时间的累加; 对于没有入度的奶牛, 找出他们当中的最大值作为结果res的初始值; 对于有入度的奶牛, 我们可以用一个数组g1把他们原始的挤奶时间存起来; 在bfs过程中, 我们用他的挤奶时间g1[j]加上它父类的挤奶时间g[t]来更新当前奶牛的g[j], 如果有多个父类则找最大值; 然后当入度为0后用把g和结果res作比较, 结果取最大值; 接着再用g去更新其他子类奶牛的挤奶时间;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5+ 10, mod = 1e9 + 7;
int n, m, k, idx, res;
int g[N], h[N], e[N], ne[N], d[N];
int g1[N];
queue<int> q;
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void bfs() {
	while (q.size()) {
		int t = q.front();
		res = max(res, g[t]);
		q.pop();
		for (int i = h[t]; i != -1; i = ne[i]) {
			int j = e[i];
			d[j]--;
			g[j] = max(g[j], g[t] + g1[j]);
			if (d[j] == 0) {
				q.push(j);
			}
		}
	}

}
signed main() {
	cin >> n >> m;
	memset(h, -1, sizeof h);
	for (int i = 1; i <= n; i++) {
		cin >> g[i];
	}
	for (int i = 1; i <= m; i++) {
		int a, b;
		cin >> a >> b;
		add(a, b);
		d[b]++;
	}
	for (int i = 1; i <= n; i++) {
		if (d[i] == 0) {
			q.push(i);
		}
		else {
			g1[i] = g[i];
		}
	}
	bfs();
	cout << res;
	return 0;
}
posted @ 2023-07-24 18:59  mostimali  阅读(11)  评论(0编辑  收藏  举报