The Preliminary Contest for ICPC Asia Xuzhou 2019 解题报告

The Preliminary Contest for ICPC Asia Xuzhou 2019

【A. Who is better?】

【题目大意】
有n个敌人,n满足:给定k个a,b,n与b模a同余
两个人轮流消灭敌人,每轮消灭敌人的数量为上一轮的1到2倍,问谁会消灭最后的敌人,即下一轮另一个人没有敌人可以消灭
先手不能消灭所有敌人

【解题思路】
对于n可以用拓展中国剩余定理进行求解
可以点这里看拓展中国剩余定理详解

谁赢为斐波那契博弈论内容,即当n为斐波那契数列时先手必败

【AC代码】

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100;
ll a[maxn], b[maxn], c[maxn];
inline ll gcd(ll a, ll b) {
	return b == 0 ? a : gcd(b, a % b);
}
inline pair<ll, ll> exgcd(ll a, ll b) {
	if (!b) return pair<ll, ll>(1, 0);
	register pair<ll, ll> temp = exgcd(b, a % b);
	return pair<ll, ll>(temp.second, temp.first - a / b * temp.second);
}
inline ll excrt(int n) {
	register ll aa = a[1], bb = b[1];
	for (register int i = 2; i <= n; ++i) {
		register ll d = aa > a[i] ? gcd(aa, a[i]) : gcd(a[i], aa);
		register pair<ll, ll> temp = exgcd(aa / d, a[i] / d);
		while (temp.first < 0) temp.first += a[i] / d;
		if ((b[i] - bb) % d) return -1;
		register ll p = a[i] / d;
		register ll lcm = aa * p;
		bb = ((temp.first * (b[i] - bb) / d % p + p) % p * aa % lcm + bb) % lcm;
		aa = lcm;
	}
	return bb;
}
int main() {
	register int k;
	scanf("%d", &k);
	for (register int i = 1; i <= k; ++i) {
		scanf("%llu%llu", &a[i], &b[i]);
	}
	register ll n = excrt(k);
	if (n == -1) {
		puts("Tankernb!");
		return 0;
	}
	c[1] = 1;
	register bool flag = false;
	for (register int i = 2; i < maxn; ++i) {
		c[i] = c[i - 1] + c[i - 2];
		if (n == c[i]) {
			flag = true;
			break;
		}
	}
	if (flag) puts("Lbnb!");
	else puts("Zgxnb!");
	return 0;
}

【B. so easy】

【题目大意】
有n个数和q个询问,每次询问有两种操作:
1.标记x
2.询问x以后第一个未标记数字

【解题思路】
正解好像是用并查集维护,这里直接set暴力过

【AC代码】

#include <bits/stdc++.h>
using namespace std;
set<int> s;
int main() {
	register int n, q;
	scanf("%d%d", &n, &q);
	while (q--) {
		register int z, x;
		scanf("%d%d", &z, &x);
		if (z == 1) s.insert(x);
		else {
			while (s.find(x) != s.end()) ++x;
			printf("%d\n", x);
		}
	}
	return 0;
}

【C. Buy Watermelon】

【题目大意】
问能否将n分为两个偶数

【解题思路】
注意特判n = 2

【AC代码】

#include <bits/stdc++.h>
using namespace std;
int main() {
	register int w;
	scanf("%d", &w);
	if (w == 2) puts("NO");
	if (w & 1) puts("NO");
	else puts("YES");
	return 0;
}

【D. Carneginon】

【题目大意】
给定串t和串s,问题根据s和t的长度分为t是否为s的子串,s是否为t的子串,s和t是否相等6种情况对应不同输出

【解题思路】
kmp裸题

【AC代码】

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int Next[maxn];
inline void getnext(string p) {
	register int l = p.length();
	register int k = -1, j = 0;
	Next[0] = -1;
	while (j < l) {
		if (k == -1 || p[j] == p[k]) {
			++j, ++k;
			if (p[j] != p[k]) {
				Next[j] = k;
			}
			else Next[j] = Next[k];
		}
		else {
			k = Next[k];
		}
	}
}
inline bool Kmpfind(string s, string p) {
	memset(Next, 0, sizeof(Next));
	getnext(p);
	register int ls = s.length();
	register int lp = p.length();
	register int k = 0, j = 0;
	while (j < ls && k < lp) {
		if (k == -1 || s[j] == p[k]) {
			++j, ++k;
		}
		else k = Next[k];
		if (k >= lp) return true;
	}
	return false;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	string T;
	cin >> T;
	register int q;
	cin >> q;
	register int lt = T.length();
	while (q--) {
		string s;
		cin >> s;
		register int ls = s.length();
		if (lt == ls) {
			if (s == T) puts("jntm!");
			else puts("friend!");
		}
		else if (ls > lt) {
			if (Kmpfind(s, T)) puts("my teacher!");
			else puts("senior!");
		}
		else {
			if (Kmpfind(T, s)) puts("my child!");
			else puts("oh, child!");
		}
	}
	return 0;
}

【E. XKC’s basketball team】

【题目大意】
给定n个数字,对于每一位,问在其右边最远且比该位至少数字大m的数字位中间隔了几位,不存在则答案为-1

【解题思路】
暴力显然超时,考虑放在树上,那么可以看做区间(i + 1, n)查询a[i] + m,可以用线段树,思路就是优先查询右子树,若右子树不存在则查询左子树

【AC代码】

#include <bits/stdc++.h>
using namespace std;
#define ls (root << 1)
#define rs (root << 1 | 1)
const int maxn = 5e6 + 10;
struct Tree {
	int l, r, val;
	int id;
}tree[maxn];
int a[maxn];
inline void build(int root, int l, int r) {
	tree[root].l = l;
	tree[root].r = r;
	if (l == r) {
		scanf("%d", &tree[root].val);
		a[l] = tree[root].val;
		tree[root].id = l;
		return;
	}
	register int mid = (l + r) >> 1;
	build(root << 1, l, mid);
	build(root << 1 | 1, mid + 1, r);
	if (tree[ls].val <= tree[rs].val) {
		tree[root].val = tree[rs].val;
		tree[root].id = tree[rs].id;
	}
	else {
		tree[root].val = tree[ls].val;
		tree[root].id = tree[ls].id;
	}
}
inline int query(int root, int l, int r, int val) {
	if (tree[root].val < val || tree[root].r < l) return -1;
	register int ans = -1;
	if (l <= tree[root].id && tree[root].id <= r) {
		ans = tree[root].id;
	}
	register int t = query(rs, l, r, val);
	if (t != -1) return t;
	ans = max(ans, query(ls, l, r, val));
	return ans;
}
int main() {
	register int n, m;
	scanf("%d%d", &n, &m);
	build(1, 1, n);
	for (register int i = 1; i < n; ++i) {
		register int t = query(1, i + 1, n, a[i] + m);
		printf("%d ", t <= i ? (-1) : t - i - 1);
	}
	puts("-1");
	return 0;
}

【G. Colorful String】

回文树,待补

【I. query】

【题目大意】
给定1-n的一个排列,有q次查询,每次问(l, r)区间内有多少对数字满足min(x, y) = gcd(x, y)

【解题思路】
min(x, y) = gcd(x, y)
容易想到x与y是倍数关系,问题在于如何查询
首先,由于给出的是1-n的一个排列,那么对于1-n中的任意一个数,该数的倍数的个数为logn,那么我们可以花nlongn的时间查询出1-n中的所有倍数关系,然后存进树状数组中,做法类似于二维偏序

但是这题有许多与一般树状数组不一样的地方,需要仔细思考

【AC代码】

#include <bits/stdc++.h>
#define lowbit(x) ((x) & (-(x)))
using namespace std;
const int maxn = 5e6 + 10;
int a[maxn];
struct Node {
	int l, r;
	int id;
	Node() :l(0), r(0), id(0) {}
	Node(int ml, int mr, int mid) :l(ml), r(mr), id(mid) {}
	inline bool operator < (const Node& c)const {
		if (l == c.l) return id < c.id;
		return l > c.l;
	}
}b[maxn];
int sum[maxn];
int ans[maxn];
inline void add(int x, int k, int n) {
	while (x <= n) {
		sum[x] += k;
		x += lowbit(x);
	}
}
inline int query(int x) {
	register int res = 0;
	while (x > 0) {
		res += sum[x];
		x -= lowbit(x);
	}
	return res;
}
int main() {
	register int n, m;
	scanf("%d%d", &n, &m);
	for (register int i = 1; i <= n; ++i) {
		register int x;
		scanf("%d", &x);
		a[x] = i;
	}
	register int cnt = 0;
	for (register int i = 1; i <= n; ++i) {
		for (register int j = (i << 1); j <= n; j += i) {
			register int x = a[i], y = a[j];
			if (x > y) swap(x, y);
			b[++cnt] = Node(x, y, 0);
		}
	}
	for (register int i = 1; i <= m; ++i) {
		++cnt;
		scanf("%d%d", &b[cnt].l, &b[cnt].r);
		b[cnt].id = i;
	}
	sort(b + 1, b + cnt + 1);
	for (register int i = 1; i <= cnt; ++i) {
		if (!b[i].id) add(b[i].r, 1, n);
		else ans[b[i].id] = query(b[i].r);
	}
	for (register int i = 1; i <= m; ++i) {
		printf("%d\n", ans[i]);
	}
	return 0;
}

【解释】
我们排序保证的为l的单调性,需要将l大的区间放在前面,因为,我们查询的函数得到的是1 - r上的sum和,如果将l小的放在前面,那么我们会将1 - r加上1,而后面会有一个(l + m, r + k),如果这是一个查询区间,那么1 - r + k会包含刚才1 - r加上的1,但这个1并不属于现在的(l + m, r + k),那么r的单调性无法保证
而且id的优先级高于r,因为如果反过来的话,假设更新区间(l, r + 1),(l, r - 1),查询区间(l, r),那么同样会导致r的单调性混乱
查询时,如果b[i].id为0,表明l和r位置上的数字互为倍数关系,那么我们需要将l - r的区间答案加上1,很好理解
如果b[i].id不为0,说明该区间为询问区间,由于区间满足单调性,那么sum(1 - r)即为答案

【M. Longest subsequence】

【题目大意】
给定字符串s和p,问s中字典序严格大于p的最长子序列的长度为多少,没有输出-1

【解题思路】
对于p[k],如果s[i] == p[k],那么标记p[k]已匹配同时记录最后一个已匹配的位置pos[p[k]] = k,如果s[i] > p[k],那么此时从i往后字典序显然大于p,答案即为k + n - i,或者如果s[i]大于最后一个已匹配的位置,那么答案即为pos[j] + n - i,一直遍历到k大于串p或i大于n

注意串s与串p不能相等

【AC代码】

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, m;
string s, p;
int pos[maxn];
bool vis[maxn];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> m >> s >> p;
	register int ans = -1, k = 0;
	for (register int i = 0; i < n; ++i) {
		for (register int j = 0; j < 26; ++j) {
			if (vis[j] && s[i] - 'a' > j) ans = max(ans, pos[j] + n - i);
		}
		if (s[i] > p[k]) ans = max(ans, k + n - i);
		else if (s[i] == p[k]) {
			vis[p[k] - 'a'] = true;
			pos[p[k] - 'a'] = k++;
		}
		if (k > m) {
			if (i != n) ans = max(ans, m + n - i); //串s与串p不能相等
			break;
		}
	}
	cout << ans << endl;
	return 0;
}
发布了40 篇原创文章 · 获赞 2 · 访问量 3228

猜你喜欢

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