2024 Xiangtan University Summer Camp-Div.2(A~D)

前言

这次比赛,打的很难受,补题也很难受,真的恶心啊!!!!

而且没人写题解(确实是一个小众的比赛),但是很难受,遂狠狠补题

正文

二度树上的染色游戏

这个题,最开始自诩不会dfs,故迟迟写不出来,结果咬咬牙用dfs一下子就写出来了

其实就是用dfs遍历全图,去找到权值最小的一条路,如果一个点只有一条路可以走或者没有路可以走(如下图),那也就算走到头了,直接返回就好。然后用到了一个带权的 dfs函数递归就出来了。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long ll;
typedef pair<int, int> llp;
int w[N];
ll sum, r;
int s[N][2];

ll mi(int u, int val) {
	val += w[u];
	int s1 = s[u][0], s2 = s[u][1];
	if (!s2)
		return val;
	return min(mi(s1, val), mi(s2, val));
}

void solve() {
	int n, u, v;
	ll ans = 1e9;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> w[i], sum += w[i];
	for (int i = 0; i < n - 1; i++) {
		cin >> u >> v;
		if (s[u][0])
			s[u][1] = v;
		else
			s[u][0] = v;
	}
	r = mi(1, 0);
	cout << sum - 2 * r;
}

int main() {
	int T;
//	cin >> T;
	T = 1;
	while (T--) {
		solve();
	}
	return 0;
}

B 小文的排列

这个题看起来简单,但其实一点也不!!!

苯人,花了4个小时删去一个If,就对了,真的痛苦!!!

首先因为m太大,我们要先排除,m>n的情况,不然之后讨论总要在意这个事

首先我们可以发现,其实这个串应该是  离散串+整串+离散串的组合(离散串是一个整串的一部分,小于整串的长度并且没有重复字符)

那么首先我们因为不知道离散串和整串的间隔在哪里,所以我们可以枚举这个节点,然后去判断这个情况是否可以,那么怎判断呢?如果每次都去整个判断后面每个长度为m的区间有没有重复,那确实太慢了,所以我们其实可以进行预处理

我们可以对每个长度为m的区间进行遍历,去判断这个区间是否合法,如果合法就记录下

其实聪明的同学都发现了,这个只需要o(n)即可,然后我们再次枚举间隔,然后就可以直接看这个点是否符合这个性质,如果符合就跳转m,看下一个点是否可以,最后我们可以找到整串的结束点,再判断后离散串是否合法即可。

易错点:

我们可以发现,对于离散串+整串+离散串,其实可以没有前后离散串,也可以没有中间整串(两个离散串在一起,也是可以的),所以对于两个离散串的情况也要进行讨论,下面是代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
typedef long long ll;
typedef pair<int, int> llp;

int main() {
	int n, m, r, l;
	cin >> n >> m;
	vector<int> a(n);
	vector<bool> y(n);
	for (int &x : a)
		cin >> x;
	for (int x : a)
		if (x > m) {
			cout << "NO" << endl;
			return 0;
		}
	unordered_map<int, int>ok, o, k, ump;
	if (n < m) {
		bool flag = true;
		bool tim = true;
		for (int i = 0; i < n; i++) {
			if (!ump[a[i]])
				ump[a[i]] = 1;
			else {
				if (tim)
					ump.clear(), ump[a[i]] = 1, tim = false;
				else {
					flag = false;
					break;
				}
			}
		}
		if (flag)
			puts("YES");
		else
			puts("NO");
		return 0;
	}
	int num = 0, t, w = 0;
	l = 0, r = m - 1;
	for (int i = 0; i < m; i++) {
		if (!ok[a[i]])
			num++;
		ok[a[i]]++;
	}
	if (num == m)
		y[0] = 1;
	while (1) {
		r++;
		if (r == n)
			break;
		ok[a[l]]--;

		if (ok[a[l]] == 0)
			num--;
		ok[a[r]]++;
		if (ok[a[r]] == 1)
			num++;
		l++;
		if (num == m)
			y[l] = 1;
	}
	for (int i = n - 1; i >= 0; i--) {
		if (k[a[i]]) {
			w = i + 1;
			break;
		}
		k[a[i]] = 1;
	}
	for (int i = 0; i < n; i++) {
		if (o[a[i]] || i >= m)
			break;
		o[a[i]] = 1;
		t = i + 1;
		while (y[t])
			t += m;
		if (t >= w) {
			cout << "YES" << endl;
			return 0;
		}
	}
	cout << "NO" << endl;
	return 0;
}

C gcd hard version

首先我们根据题意可以看到一个性质,GCD会随着区间长度增加而减小,而SUM会随着区间长度增加而增大,所以我们需要找到最小的满足区间,那其他情况也就满足了

所以我们可以枚举左端点,看右端点在哪里是最小的,这里我们用二分进行查找右端点

区间GCD可以使用ST表进行查找

易错点:我们这是一种二分答案的过程,但是其实右端点实际上可能不存在,所以我们要进行一些小小的操作,我这里吧r=n+1,如果不存在r,那么r就会一直保持n+1,对最后答案没有影响

  

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
typedef pair<int, int> llp;
ll a[N], b[N], sum[N], n;
ll st[N][20];

ll query(int l, int r) {
	int k = log2(r - l + 1);
	return __gcd(st[l][k], st[r - (1 << k) + 1][k]);
}

void init() {
	for (int i = 1; i <= n; i++) {
		st[i][0] = a[i];
	}
	for (int j = 1; j <= log2(n); j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			st[i][j] = __gcd(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
		}
	}
	for (int i = 1; i <= n; i++) {
		sum[i] = sum[i - 1] + b[i];
	}
}

int solve(int i) {
	ll m, r = n + 1, l = i ;
	ll s, g;
	while (l < r) {
		m = (l + r) >> 1;
		g = query(i, m);
		s = sum[m] - sum[i - 1];
		if (g <= s)
			r = m;
		else
			l = m + 1;
	}
	g = query(i, r);
	s = sum[r] - sum[i - 1];
	if (g <= s)
		return r;
	else
		return n + 1;
}

int main() {
	ll ans = 0;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++)
		cin >> b[i];
	init();
	for (int i = 1; i <= n; i++) {
		ans += n - solve(i) + 1;
	}
	cout << ans;
	return 0;
}

D 战至终章

这个题有一点长,但是解决起来比较简单,首先我们可以看到这个其实是一个拓扑,就是只有打败了一些怪物,才能挑战另外的boos,但是我们又需要打得过,所以对于能打的Boos尽可能打。

我们可以创建一个优先队列priority_queue,以战力排升序

这里要注意下,关于priority_queue,首先他是大根堆,也就是默认降序,而且不能像sort一样加个cmp函数,所以我们要重载小于号

如何重载呢?

细细品味就有深意,然后就是把已经解锁了的boos放进priority_queue里,直到打不过了,或者打没了

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<int, int> llp;

struct nobe {
	int a, b, r, num;
	vector<int>c;
} d[N];

bool operator <(const nobe &x, const nobe &y) {
	return x.a > y.a;
}
priority_queue<nobe>aa;

int main() {
	ll n, p, k, x, w = 0;
	vector<int>ans;
	cin >> n >> p;
	for (int i = 1; i <= n; i++) {
		cin >> d[i].a;
		d[i].num = i;
	}
	for (int i = 1; i <= n; i++) {
		cin >> d[i].b;
	}
	for (int i = 1; i <= n; i++) {
		cin >> k;
		d[i].r = k;
		while (k--) {
			cin >> x;
			d[x].c.push_back(i);
		}
	}
	for (int i = 1; i <= n; i++)
		if (!d[i].r)
			aa.push(d[i]);
	while (!aa.empty()) {
		nobe nn = aa.top();
		aa.pop();
		if (nn.a <= p) {
			w++;
			ans.push_back(nn.num);
			p += nn.b;
			for (auto xx : nn.c) {
				d[xx].r--;
				if (d[xx].r == 0)
					aa.push(d[xx]);
			}
		}
	}
	sort(ans.begin(), ans.end());
	cout << w << endl;
	for (auto xx : ans)
		cout << xx << ' ';
	return 0;
}

后记

还有两个题没补,看情况补了,因为这种不给数据,没有代码题解,也没几个人打的比赛,真的debug太痛苦了,真的。不过有机会还是会补的,看情况吧

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值