A - Yet Another Dividing into Teams

题意:n个不同数,分尽可能少的组,要求组内没有两个人的差恰为1。

题解:奇偶分组。

  1. int a[200005];
  2. void test_case() {
  3. int n;
  4. scanf("%d", &n);
  5. for(int i = 1; i <= n; ++i)
  6. scanf("%d", &a[i]);
  7. sort(a + 1, a + 1 + n);
  8. int ans = 1;
  9. for(int i = 2; i <= n; ++i) {
  10. if(a[i] == a[i - 1] + 1)
  11. ans = 2;
  12. }
  13. printf("%d\n", ans);
  14. }

B1 - Books Exchange (hard version)

见下

B2 - Books Exchange (hard version)

题意:有一个排列,求每个位置的环的大小。

题解:并查集或者直接dfs。

  1. int p[200005];
  2. int ans[200005];
  3. int cnt;
  4. void dfs(int u, int t) {
  5. if(u == t)
  6. return;
  7. ++cnt;
  8. dfs(p[u], t);
  9. ans[u] = cnt;
  10. }
  11. void test_case() {
  12. int n;
  13. scanf("%d", &n);
  14. for(int i = 1; i <= n; ++i) {
  15. scanf("%d", &p[i]);
  16. ans[i] = 0;
  17. }
  18. for(int i = 1; i <= n; ++i) {
  19. if(!ans[i]) {
  20. cnt = 1;
  21. dfs(p[i], i);
  22. ans[i] = cnt;
  23. }
  24. printf("%d%c", ans[i], " \n"[i == n]);
  25. }
  26. }

C1 - Good Numbers (easy version)

见下

C2 - Good Numbers (hard version)

题意:一个good number是有一系列互异的3的幂次的和构成的,求超过n的最小的good number。

题解:这么说每种幂次都至多出现1次,先让大家都出现,然后优先去掉最大的幂次。

  1. ll p3[105];
  2. void test_case() {
  3. p3[0] = 1;
  4. int top = 0;
  5. while(p3[top] < 1e18)
  6. p3[++top] = p3[top - 1] * 3ll;
  7. ll sum = 0;
  8. for(int i = 0; i <= top; ++i) {
  9. //cout<<p3[i]<<endl;
  10. sum += p3[i];
  11. }
  12. int q;
  13. scanf("%d", &q);
  14. while(q--) {
  15. ll n;
  16. scanf("%lld", &n);
  17. ll tsum = sum;
  18. for(int i = top; i >= 0; --i) {
  19. if(tsum - p3[i] >= n)
  20. tsum -= p3[i];
  21. }
  22. printf("%lld\n", tsum);
  23. }
  24. }

D1 - Too Many Segments (easy version)

见下

D2 - Too Many Segments (hard version)

题意:给一个数轴,上面有n个线段,移除最少的线段使得没有任何一个点被覆盖超过k次。(包括端点)

题解:看起来可以口胡一个贪心,先用线段树update出每个点的被覆盖次数,然后在每个点记录覆盖当前点的最右的线段是哪个(不过这样好像会记录太多线段),从左往右扫描,当一个点超过k时就移除最右的那条线段。这个做法的问题在于不能维护最右的线段是哪个。不过我们从左往右扫的时候遇到左端点就把线段的右端点插进set里面,然后遇到线段的右端点就可以把它移除(其实不移除也可以)。仔细想想甚至不需要线段树,也不需要set。

  1. int n, k;
  2. int l[200005], r[200005];
  3. vector<int> L[200005], R[200005];
  4. const int N = 200000;
  5. bool del[200005];
  6. priority_queue<pii> S;
  7. vector<int> ans;
  8. void test_case() {
  9. scanf("%d%d", &n, &k);
  10. for(int i = 1; i <= n; ++i) {
  11. scanf("%d%d", &l[i], &r[i]);
  12. L[l[i]].push_back(i);
  13. R[r[i]].push_back(i);
  14. }
  15. int cnt = 0;
  16. for(int i = 1; i <= N; ++i) {
  17. for(auto &t : L[i]) {
  18. ++cnt;
  19. S.push({r[t], t});
  20. }
  21. while(cnt > k) {
  22. --cnt;
  23. int t = S.top().second;
  24. S.pop();
  25. del[t] = 1;
  26. ans.push_back(t);
  27. }
  28. for(auto &t : R[i]) {
  29. if(!del[t])
  30. --cnt;
  31. }
  32. }
  33. printf("%d\n", (int)ans.size());
  34. for(auto &t : ans)
  35. printf("%d ", t);
  36. printf("\n");
  37. }

E - By Elevator or Stairs?

题意:有一个n层建筑,求从第1层去第i层的最短时间。你可以选择走楼梯,或者支付一个等待时间之后走电梯。

题解:那只需要记录前一层最后在楼梯和在电梯的最小花费直接转移就行了。

大意了,没看范围就交了,还好不会溢出。

  1. int a[200005];
  2. int b[200005];
  3. void test_case() {
  4. int n, c;
  5. scanf("%d%d", &n, &c);
  6. for(int i = 1; i <= n - 1; ++i)
  7. scanf("%d", &a[i]);
  8. for(int i = 1; i <= n - 1; ++i)
  9. scanf("%d", &b[i]);
  10. int dp0 = 0, dp1 = c;
  11. for(int i = 1; i <= n; ++i) {
  12. if(i > 1) {
  13. int ndp0 = min(dp0, dp1) + a[i - 1];
  14. int ndp1 = min(dp0 + c + b[i - 1], dp1 + b[i - 1]);
  15. dp0 = ndp0;
  16. dp1 = ndp1;
  17. }
  18. printf("%d%c", min(dp0, dp1), " \n"[i == n]);
  19. }
  20. }

F - Maximum Weight Subset

题意:在一棵树上面找一个点集,使得点集的权重和最大且点集中的点两两之间的距离严格大于k。

题解:感觉是可以树形dp的,设dp[i][j]为以结点i为根的子树,最近的被选择点距离为j的最大的权值。当i被选择时j就是0;否则当i的儿子被选择时j就是1。

但是子树之间的选择是比较复杂的。先把第一棵子树之间合并到根上:dp[u][j+1]=dp[i][j],且dp[u][0]=dp[i][k]。

再遍历u的其他子树i,距离为j的子树可以向根的距离t>=k-j配对,这时尝试更新dp[u][min(j+1,t)]。

看起来是个n^3的dp。

但是这个实际上有一些细节,比方说我这棵子树只有很底部的选了,而根节点不选,这个时候距离是>=k+1的状态,这些应该统一存起来。

  1. int n, k;
  2. int w[205];
  3. int dp[205][205];
  4. vector<int>G[205];
  5. void dfs(int u, int p) {
  6. if(G[u].size() == 1 && p != -1) {
  7. dp[u][0] = w[u];
  8. return;
  9. }
  10. bool first = 1;
  11. for(int i = 0; i < G[u].size(); ++i) {
  12. int v = G[u][i];
  13. if(v == p)
  14. continue;
  15. dfs(v, u);
  16. if(first) {
  17. for(int j = 0; j + 1 <= k; ++j)
  18. dp[u][j + 1] = dp[v][j];
  19. dp[u][0] = w[u] + dp[v][k];
  20. first = 0;
  21. } else {
  22. for(int j = 0; j <= k; ++j) {
  23. for(int t = k - j; t <= k; ++t)
  24. dp[u][min(j + 1, t)] = max(dp[u][min(j + 1, t)], dp[u][t] + dp[v][j]);
  25. }
  26. }
  27. }
  28. /*for(int j = 0; j <= k; ++j)
  29. printf("dp[%d][%d]=%d\n", u, j, dp[u][j]);*/
  30. }
  31. void test_case() {
  32. scanf("%d%d", &n, &k);
  33. for(int i = 1; i <= n; ++i)
  34. scanf("%d", &w[i]);
  35. if(n == 1) {
  36. printf("%d\n", w[1]);
  37. return;
  38. }
  39. for(int i = 1; i <= n - 1; ++i) {
  40. int u, v;
  41. scanf("%d%d", &u, &v);
  42. G[u].push_back(v);
  43. G[v].push_back(u);
  44. }
  45. dfs(1, -1);
  46. int ans = 0;
  47. for(int j = 0; j <= k; ++j)
  48. ans = max(ans, dp[1][j]);
  49. printf("%d\n", ans);
  50. }

上面错在合并子树的时候可能同一棵子树重复更新了根节点的某个距离,导致状态与实际不符,解决的办法是往临时数组中更新,然后再把临时数组写回去(类似滚动数组)。而且状态设为dp[i][j]为以i为根的子树最近一个被选择点的距离超过j的最大值:

  1. int n, k;
  2. int w[205];
  3. int dp[205][205];
  4. int tmp[205];
  5. vector<int>G[205];
  6. void dfs(int u, int p) {
  7. if(G[u].size() == 1 && p != -1) {
  8. dp[u][0] = w[u];
  9. return;
  10. }
  11. bool first = 1;
  12. for(int i = 0; i < G[u].size(); ++i) {
  13. int v = G[u][i];
  14. if(v == p)
  15. continue;
  16. dfs(v, u);
  17. if(first) {
  18. dp[u][0] = w[u] + dp[v][k];
  19. for(int j = 0; j <= k; ++j)
  20. dp[u][j + 1] = dp[v][j];
  21. for(int j = k; j >= 0; --j)
  22. dp[u][j] = max(dp[u][j + 1], dp[u][j]);
  23. first = 0;
  24. } else {
  25. tmp[0] = dp[u][0] + dp[v][k];
  26. for(int j = 0; j <= k; ++j)
  27. tmp[j + 1] = dp[v][j];
  28. for(int j = 0; j <= k; ++j) {
  29. for(int t = k - j; t <= k + 1; ++t)
  30. tmp[min(j + 1, t)] = max(tmp[min(j + 1, t)], dp[u][t] + dp[v][j]);
  31. }
  32. for(int j = k; j >= 0; --j)
  33. tmp[j] = max(tmp[j + 1], tmp[j]);
  34. for(int j = 0; j <= k + 1; ++j)
  35. dp[u][j] = max(dp[u][j], tmp[j]);
  36. }
  37. }
  38. /*for(int j = 0; j <= k; ++j)
  39. printf("dp[%d][%d]=%d\n", u, j, dp[u][j]);
  40. puts("");*/
  41. }
  42. void test_case() {
  43. scanf("%d%d", &n, &k);
  44. for(int i = 1; i <= n; ++i)
  45. scanf("%d", &w[i]);
  46. if(n == 1) {
  47. printf("%d\n", w[1]);
  48. return;
  49. }
  50. for(int i = 1; i <= n - 1; ++i) {
  51. int u, v;
  52. scanf("%d%d", &u, &v);
  53. G[u].push_back(v);
  54. G[v].push_back(u);
  55. }
  56. dfs(1, -1);
  57. int ans = 0;
  58. for(int j = 0; j <= k + 1; ++j)
  59. ans = max(ans, dp[1][j]);
  60. printf("%d\n", ans);
  61. }

假如设为恰好等于的话是这样的,也是要用k+1表示大于等于k的所有值,在这里截断。和南京区域赛的那个银牌题差不多。

  1. int n, k;
  2. int w[205];
  3. int dp[205][205];
  4. int tmp[205];
  5. vector<int>G[205];
  6. void dfs(int u, int p) {
  7. if(G[u].size() == 1 && p != -1) {
  8. dp[u][0] = w[u];
  9. return;
  10. }
  11. bool first = 1;
  12. for(int i = 0; i < G[u].size(); ++i) {
  13. int v = G[u][i];
  14. if(v == p)
  15. continue;
  16. dfs(v, u);
  17. if(first) {
  18. dp[u][0] = w[u] + max(dp[v][k], dp[v][k + 1]);
  19. for(int j = 0; j <= k - 1; ++j)
  20. dp[u][j + 1] = dp[v][j];
  21. dp[u][k + 1] = max(dp[v][k], dp[v][k + 1]);
  22. first = 0;
  23. } else {
  24. tmp[0] = dp[u][0] + max(dp[v][k], dp[v][k + 1]);
  25. for(int j = 0; j <= k - 1; ++j)
  26. tmp[j + 1] = dp[v][j];
  27. tmp[k + 1] = max(dp[v][k], dp[v][k + 1]);
  28. for(int j = 0; j <= k + 1; ++j) {
  29. for(int t = max(0, k - j); t <= k + 1; ++t)
  30. tmp[min(j + 1, t)] = max(tmp[min(j + 1, t)], dp[u][t] + dp[v][j]);
  31. }
  32. for(int j = 0; j <= k + 1; ++j)
  33. dp[u][j] = max(dp[u][j], tmp[j]);
  34. }
  35. }
  36. /*for(int j = 0; j <= k; ++j)
  37. printf("dp[%d][%d]=%d\n", u, j, dp[u][j]);
  38. puts("");*/
  39. }
  40. void test_case() {
  41. scanf("%d%d", &n, &k);
  42. for(int i = 1; i <= n; ++i)
  43. scanf("%d", &w[i]);
  44. if(n == 1) {
  45. printf("%d\n", w[1]);
  46. return;
  47. }
  48. for(int i = 1; i <= n - 1; ++i) {
  49. int u, v;
  50. scanf("%d%d", &u, &v);
  51. G[u].push_back(v);
  52. G[v].push_back(u);
  53. }
  54. dfs(1, -1);
  55. int ans = 0;
  56. for(int j = 0; j <= k + 1; ++j)
  57. ans = max(ans, dp[1][j]);
  58. printf("%d\n", ans);
  59. }

不要漏掉了j=k+1状态的转移。也就是一开始设状态的时候就要考虑到有个k+1表示距离>=k+1的点。

总结:一题很好玩的树形dp。注意:1、树上度为1的虽然都是叶子,但是假如是根的话也要继续往下走。2、不要在原地更新,要另外开一个tmp数组保存。3、即使设状态为dp[i][j]为以i为根的子树最近一个被选择点的距离超过j的最大值,在合并子树的时候也要注意虽然选择t的时候是选择高的位置更新会得到更大的答案,但事实上j可以更新到一片连续的t。

Codeforces Round #595 (Div. 3)的更多相关文章

  1. Codeforces Round #595 (Div. 3)D1D2 贪心 STL

    一道用STL的贪心,正好可以用来学习使用STL库 题目大意:给出n条可以内含,相交,分离的线段,如果重叠条数超过k次则为坏点,n,k<2e5 所以我们贪心的想我们从左往右遍历,如果重合部分条数超 ...

  2. Codeforces Round #595 (Div. 3)B2 简单的dfs

    原题 https://codeforces.com/contest/1249/problem/B2 这道题一开始给的数组相当于地图的路标,我们只需对每个没走过的点进行dfs即可 #include &l ...

  3. Codeforces Round #595 (Div. 3) D2Too Many Segments,线段树

    题意:给n个线段,每个线段会覆盖一些点,求删最少的线段,使得每个点覆盖的线段不超过k条. 思路:按右端点排序,之后依次加入每个线段,查询线段覆盖区间内的每个点,覆盖的最大线段数量,如果不超过k,那就可 ...

  4. Codeforces Round #595 (Div. 3) A,B,C,D

    A题:n个学生,分成任意组,要求每组中任意两名学生的技能值相差不等于1,问最小分组. #include<bits/stdc++.h> using namespace std; #defin ...

  5. Codeforces Round #595 (Div. 3) 题解

    前言 大家都在洛谷上去找原题吧,洛谷还是不错的qwq A 因为没有重复的数,我们只要将数据排序,比较两两之间有没有\(a_j - a_i == 1 (j > i)\) 的,有则输出 \(2\) ...

  6. Codeforces Round #366 (Div. 2) ABC

    Codeforces Round #366 (Div. 2) A I hate that I love that I hate it水题 #I hate that I love that I hate ...

  7. Codeforces Round #354 (Div. 2) ABCD

    Codeforces Round #354 (Div. 2) Problems     # Name     A Nicholas and Permutation standard input/out ...

  8. Codeforces Round #368 (Div. 2)

    直达–>Codeforces Round #368 (Div. 2) A Brain’s Photos 给你一个NxM的矩阵,一个字母代表一种颜色,如果有”C”,”M”,”Y”三种中任意一种就输 ...

  9. cf之路,1,Codeforces Round #345 (Div. 2)

     cf之路,1,Codeforces Round #345 (Div. 2) ps:昨天第一次参加cf比赛,比赛之前为了熟悉下cf比赛题目的难度.所以做了round#345连试试水的深浅.....   ...

随机推荐

  1. python爬虫-有道翻译-js加密破解

    有道翻译-js加密破解 这是本地爬取的网址:http://fanyi.youdao.com/ 一.分析请求 我们在页面中输入:水果,翻译后的英文就是:fruit.请求携带的参数有很多,先将参数数据保存 ...

  2. aria2 资料

    https://www.jianshu.com/p/8124b5b6ef95https://quan.ithome.com/0/331/853.htmhttp://www.360doc.com/con ...

  3. 用navicat操作oracle新建表空间、用户名、密码

    转载从:https://www.cnblogs.com/franson-2016/p/5925593.html 首先.我们来新建一个表空间.打开Navicat for Oracle,输入相关的的连接信 ...

  4. PHP中的十进制、八进制、二进制、十六进制

    我们平时用的都是十进制. 比如:987这个数字,其本质就是7*10^0+8*10^1+9*10^2 个位数上的7,1就是1,十位上的8,1就是10,百位上的9,1是100 echo '<br&g ...

  5. 自定义View(一),初识自定义View

    看了无数资料,总结一下自定义View 先明白一个自定义View的三大流程 onMeasure() 测量,决定View的大小 onLayout() 布局,决定View在ViewGroup中的位置 onD ...

  6. 在CentOS 7上修改主机名的方法

    这次我们来讲解一下如何在CentOS 7环境上修改主机名 1.从VMware上登录CentOS 7的虚拟机,并以root用户登录. 2.查看未修改前的主机名 1>.我们可以通过文件hostnam ...

  7. 判断CPU大小端示例代码

    #include <stdio.h> int checkCPU() { union w{ int a; char b; }c; c.a = ; ); } int main() { prin ...

  8. 存储型XSS靶场作业

    首先进入靶场:http://59.63.200.79:8082/index.php xss平台使用:xss8c 发现CMS版本号,搜索是否此版本号存在可利用漏洞: 找到存储型xss漏洞,在xss平台生 ...

  9. YES, There is No such thing as a free lunch

    软件行业本身就建立在copy的基础上的,据说视窗both Windows and Mac OS都借鉴了施乐的. 国内的很多的软件质量真的好差呀. https://queue.acm.org/detai ...

  10. mybatis多数据库切换,(动态数据源)。

    项目中将一个库的某些标的某些数据保存到另一个库. 使用spring的aop编程动态切换数据源,代码如下,以备下次用到! 1.先将两个数据库连接,创建两个数据源,交于spring管理! <bean ...