前言
这次比赛,打的很难受,补题也很难受,真的恶心啊!!!!
而且没人写题解(确实是一个小众的比赛),但是很难受,遂狠狠补题
正文
A 二度树上的染色游戏
这个题,最开始自诩不会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太痛苦了,真的。不过有机会还是会补的,看情况吧