# 题目传送门

## A .Yet Another Dividing into Teams

sol：原先是用比较复杂的方法来解的，后来学弟看了一眼，发现不是1就是2，当出现两个人水平相差为1就分成两组，1组全是奇数，1组全是偶数，必然符合题意。

• 思维
#include "bits/stdc++.h"
using namespace std;
#define debug puts("what the fuck");
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 110;
bool vis[MAXN];
int main() {
int t, n, m;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
bool ok = true;
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n; i++) {
scanf("%d", &m);
vis[m] = true;
if (vis[m - 1] || vis[m + 1]) ok = false;
}
if (ok) puts("1");
else puts("2");
}
return 0;
}
View Code

## B. Books Exchange

sol：很裸的并查集，i和p[i]属于同一个集合，求每个点所在集合的大小

• 并查集
#include "bits/stdc++.h"
using namespace std;
#define debug puts("what the fuck");
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 2e5 + 10;
int pre[MAXN], cnt[MAXN];
int find(int i) {
if (pre[i] == i) return i;
return pre[i] = find(pre[i]);
}
int main() {
int t, n, m;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
cnt[i] = 1;
pre[i] = i;
}
for (int i = 1; i <= n; i++) {
scanf("%d", &m);
int u = find(i);
int v = find(m);
if (u != v) {
pre[u] = v;
cnt[v] += cnt[u];
}
}
for (int i = 1; i <= n; i++)
printf("%d ", cnt[find(i)]);
puts("");
}
return 0;
}
View Code

## C1. Good Numbers (easy version)

sol：由于n比较小，所以我们可以暴力罗列good number，pow(3, 10)已经大于n的最大范围10000，所以罗列到3的10次方够了，再组合一下，排序后用二分去搜就好了。

• 暴力组合
#include "bits/stdc++.h"
using namespace std;
#define debug puts("what the fuck");
typedef long long LL;
typedef pair<int, int> PII;
int p[20] = {1};
vector<int> v;
void init() {
for (int i = 1; i <= 10; i++)
p[i] = 3 * p[i - 1];
for (int i = 1; i <= (1 << 10); i++) {
int sum = 0;
for (int j = 0; j <= 10; j++)
if (i & (1 << j)) sum += p[j];
v.push_back(sum);
}
}
int main() {
int t, n;
scanf("%d", &t);
init();
sort(v.begin(), v.end());
while (t--) {
scanf("%d", &n);
printf("%d\n", *lower_bound(v.begin(), v.end(), n));
}
return 0;
}
View Code

## C2. Good Numbers (hard version)

sol：细看这题感觉和进制有关，把n转成3进制后找到第一个大于等于n并且所以三进制位不为2的数；

• 进制
#include "bits/stdc++.h"
using namespace std;
#define debug puts("what the fuck");
typedef long long LL;
typedef pair<int, int> PII;
int p[60];
int main() {
int t;
LL n, pos;
scanf("%d", &t);
while (t--) {
scanf("%lld", &n);
memset(p, 0, sizeof(p));
pos = 0;
while (n) {
p[pos++] = n % 3;
n /= 3;
}
int k = pos - 1;
while (k > 0 && p[k] != 2) k--; // 从高位往低位搜找到第一个2
for (int i = k - 1; i >= 0; i--) p[i] = 0; // 第k位加1,那么k之前的位可以全部为0了,一定不会小于n
while (p[k] == 2) { //进位
p[k] = 0;
p[++k] ++;
}
pos = max(pos, k + 1LL);
LL sum = 0, pp = 1;
for (int i = 0; i < pos; i++) {
if (p[i] == 1) sum += pp;
pp *= 3;
}
printf("%lld\n", sum);
}
return 0;
}
View Code

## D. Too Many Segments

sol：用差分确定覆盖每个点的线段有多少条，对于点i，如果覆盖的线段数量大于k，删掉覆盖该点的右端点最远的线段，可以用优先队列找这样的线段。就是需要两个排序方式。

• 贪心，差分
#include "bits/stdc++.h"
using namespace std;
#define debug puts("what the fuck");
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 2e5 + 10;
struct Seg {
int l, r;
int index;
friend bool operator < (Seg a, Seg b) {return a.r < b.r;}
} a[MAXN];
priority_queue<Seg> que;
bool cmp(Seg a, Seg b) {return a.l < b.l;}
vector<int> ans;
int cnt[MAXN], pos, sum;
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i].l, &a[i].r);
a[i].index = i;
}
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
while (pos < a[i].l) sum += cnt[++pos];
que.push(a[i]);
sum ++, cnt[a[i].r + 1] --;
if (sum > m) {
sum --;
Seg s = que.top();
que.pop();
cnt[s.r + 1] ++;
ans.push_back(s.index);
}
}
printf("%d\n", ans.size());
for (auto index : ans) printf("%d ", index);
return 0;
}
View Code

## E. By Elevator or Stairs?

sol：dp，dp[0][i]表示从a[i]到1的最少时间，dp[1][i]表示从b[i]到1的最少时间。代码中直接把a、b数组当dp[0]和dp[1]来用了；

• 动态规划
#include "bits/stdc++.h"
using namespace std;
#define debug puts("what the fuck");
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 2e5 + 10;
int a[MAXN], b[MAXN];
int main() {
int n, c;
scanf("%d%d", &n, &c);
a[1] = 0, b[1] = c;
for (int i = 2; i <= n; i++) scanf("%d", &a[i]);
for (int i = 2; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1; i <= n; i++) {
a[i] += min(a[i - 1], b[i - 1]);
b[i] += min(a[i - 1] + c, b[i - 1]);
printf("%d ", min(a[i], b[i]));
}
return 0;
}
View Code

## F. Maximum Weight Subset

sol：树形dp，dp[u][dep]表示u子树中距离u最近的选取点离u的距离不小于dep的最大权重。要注意的是合并的时候两颗子树间的选取点距离也不能小于等于k；

• 树形dp
#include "bits/stdc++.h"
using namespace std;
#define debug puts("what the fuck");
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 210;
int w[MAXN];
int dp[MAXN][MAXN];
vector<int> edge[MAXN];
int n, k;
void dfs(int u, int fa) {
for (auto v : edge[u]) {
if (v == fa) continue;
dfs(v, u);
}
dp[u][0] = w[u];
for (auto v : edge[u]) {
if (v == fa) continue;
dp[u][0] += dp[v][k];
}
for (int dep = 1; dep <= k; dep++) {
for (auto v : edge[u]) {
if (v == fa) continue;
int sum = dp[v][dep - 1];
for (auto other : edge[u]) {
if (other == fa || other == v) continue;
sum += dp[other][max(dep - 1, k - dep)];
}
dp[u][dep] = max(dp[u][dep], sum);
}
}
for (int i = k - 1; i >= 0; i--) dp[u][i] = max(dp[u][i], dp[u][i + 1]);
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
for (int i = 2; i <= n; i++) {
int u, v;
scanf("%d%d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(1, -1);
printf("%d\n", dp[1][0]);
return 0;
}
View Code

官方题解里vector太多了，看的好懵逼。提交最后解释了一下为什么复杂度是O(n^3)不是O(n^4)，但是我看着还是感觉复杂度是O(n^4)

——————————————————————————————————————————————————————————————

posted @ 2019-10-24 15:06  Angel&Demon  阅读(...)  评论(...编辑  收藏