Codeforces Round #595 (Div. 3)
A - Yet Another Dividing into Teams
题意:n个不同数,分尽可能少的组,要求组内没有两个人的差恰为1。
题解:奇偶分组。
int a[200005];
void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
int ans = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] == a[i - 1] + 1)
ans = 2;
}
printf("%d\n", ans);
}
B1 - Books Exchange (hard version)
见下
B2 - Books Exchange (hard version)
题意:有一个排列,求每个位置的环的大小。
题解:并查集或者直接dfs。
int p[200005];
int ans[200005];
int cnt;
void dfs(int u, int t) {
if(u == t)
return;
++cnt;
dfs(p[u], t);
ans[u] = cnt;
}
void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &p[i]);
ans[i] = 0;
}
for(int i = 1; i <= n; ++i) {
if(!ans[i]) {
cnt = 1;
dfs(p[i], i);
ans[i] = cnt;
}
printf("%d%c", ans[i], " \n"[i == n]);
}
}
C1 - Good Numbers (easy version)
见下
C2 - Good Numbers (hard version)
题意:一个good number是有一系列互异的3的幂次的和构成的,求超过n的最小的good number。
题解:这么说每种幂次都至多出现1次,先让大家都出现,然后优先去掉最大的幂次。
ll p3[105];
void test_case() {
p3[0] = 1;
int top = 0;
while(p3[top] < 1e18)
p3[++top] = p3[top - 1] * 3ll;
ll sum = 0;
for(int i = 0; i <= top; ++i) {
//cout<<p3[i]<<endl;
sum += p3[i];
}
int q;
scanf("%d", &q);
while(q--) {
ll n;
scanf("%lld", &n);
ll tsum = sum;
for(int i = top; i >= 0; --i) {
if(tsum - p3[i] >= n)
tsum -= p3[i];
}
printf("%lld\n", tsum);
}
}
D1 - Too Many Segments (easy version)
见下
D2 - Too Many Segments (hard version)
题意:给一个数轴,上面有n个线段,移除最少的线段使得没有任何一个点被覆盖超过k次。(包括端点)
题解:看起来可以口胡一个贪心,先用线段树update出每个点的被覆盖次数,然后在每个点记录覆盖当前点的最右的线段是哪个(不过这样好像会记录太多线段),从左往右扫描,当一个点超过k时就移除最右的那条线段。这个做法的问题在于不能维护最右的线段是哪个。不过我们从左往右扫的时候遇到左端点就把线段的右端点插进set里面,然后遇到线段的右端点就可以把它移除(其实不移除也可以)。仔细想想甚至不需要线段树,也不需要set。
int n, k;
int l[200005], r[200005];
vector<int> L[200005], R[200005];
const int N = 200000;
bool del[200005];
priority_queue<pii> S;
vector<int> ans;
void test_case() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &l[i], &r[i]);
L[l[i]].push_back(i);
R[r[i]].push_back(i);
}
int cnt = 0;
for(int i = 1; i <= N; ++i) {
for(auto &t : L[i]) {
++cnt;
S.push({r[t], t});
}
while(cnt > k) {
--cnt;
int t = S.top().second;
S.pop();
del[t] = 1;
ans.push_back(t);
}
for(auto &t : R[i]) {
if(!del[t])
--cnt;
}
}
printf("%d\n", (int)ans.size());
for(auto &t : ans)
printf("%d ", t);
printf("\n");
}
E - By Elevator or Stairs?
题意:有一个n层建筑,求从第1层去第i层的最短时间。你可以选择走楼梯,或者支付一个等待时间之后走电梯。
题解:那只需要记录前一层最后在楼梯和在电梯的最小花费直接转移就行了。
大意了,没看范围就交了,还好不会溢出。
int a[200005];
int b[200005];
void test_case() {
int n, c;
scanf("%d%d", &n, &c);
for(int i = 1; i <= n - 1; ++i)
scanf("%d", &a[i]);
for(int i = 1; i <= n - 1; ++i)
scanf("%d", &b[i]);
int dp0 = 0, dp1 = c;
for(int i = 1; i <= n; ++i) {
if(i > 1) {
int ndp0 = min(dp0, dp1) + a[i - 1];
int ndp1 = min(dp0 + c + b[i - 1], dp1 + b[i - 1]);
dp0 = ndp0;
dp1 = ndp1;
}
printf("%d%c", min(dp0, dp1), " \n"[i == n]);
}
}
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的状态,这些应该统一存起来。
int n, k;
int w[205];
int dp[205][205];
vector<int>G[205];
void dfs(int u, int p) {
if(G[u].size() == 1 && p != -1) {
dp[u][0] = w[u];
return;
}
bool first = 1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v == p)
continue;
dfs(v, u);
if(first) {
for(int j = 0; j + 1 <= k; ++j)
dp[u][j + 1] = dp[v][j];
dp[u][0] = w[u] + dp[v][k];
first = 0;
} else {
for(int j = 0; j <= k; ++j) {
for(int t = k - j; t <= k; ++t)
dp[u][min(j + 1, t)] = max(dp[u][min(j + 1, t)], dp[u][t] + dp[v][j]);
}
}
}
/*for(int j = 0; j <= k; ++j)
printf("dp[%d][%d]=%d\n", u, j, dp[u][j]);*/
}
void test_case() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &w[i]);
if(n == 1) {
printf("%d\n", w[1]);
return;
}
for(int i = 1; i <= n - 1; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, -1);
int ans = 0;
for(int j = 0; j <= k; ++j)
ans = max(ans, dp[1][j]);
printf("%d\n", ans);
}
上面错在合并子树的时候可能同一棵子树重复更新了根节点的某个距离,导致状态与实际不符,解决的办法是往临时数组中更新,然后再把临时数组写回去(类似滚动数组)。而且状态设为dp[i][j]为以i为根的子树最近一个被选择点的距离超过j的最大值:
int n, k;
int w[205];
int dp[205][205];
int tmp[205];
vector<int>G[205];
void dfs(int u, int p) {
if(G[u].size() == 1 && p != -1) {
dp[u][0] = w[u];
return;
}
bool first = 1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v == p)
continue;
dfs(v, u);
if(first) {
dp[u][0] = w[u] + dp[v][k];
for(int j = 0; j <= k; ++j)
dp[u][j + 1] = dp[v][j];
for(int j = k; j >= 0; --j)
dp[u][j] = max(dp[u][j + 1], dp[u][j]);
first = 0;
} else {
tmp[0] = dp[u][0] + dp[v][k];
for(int j = 0; j <= k; ++j)
tmp[j + 1] = dp[v][j];
for(int j = 0; j <= k; ++j) {
for(int t = k - j; t <= k + 1; ++t)
tmp[min(j + 1, t)] = max(tmp[min(j + 1, t)], dp[u][t] + dp[v][j]);
}
for(int j = k; j >= 0; --j)
tmp[j] = max(tmp[j + 1], tmp[j]);
for(int j = 0; j <= k + 1; ++j)
dp[u][j] = max(dp[u][j], tmp[j]);
}
}
/*for(int j = 0; j <= k; ++j)
printf("dp[%d][%d]=%d\n", u, j, dp[u][j]);
puts("");*/
}
void test_case() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &w[i]);
if(n == 1) {
printf("%d\n", w[1]);
return;
}
for(int i = 1; i <= n - 1; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, -1);
int ans = 0;
for(int j = 0; j <= k + 1; ++j)
ans = max(ans, dp[1][j]);
printf("%d\n", ans);
}
假如设为恰好等于的话是这样的,也是要用k+1表示大于等于k的所有值,在这里截断。和南京区域赛的那个银牌题差不多。
int n, k;
int w[205];
int dp[205][205];
int tmp[205];
vector<int>G[205];
void dfs(int u, int p) {
if(G[u].size() == 1 && p != -1) {
dp[u][0] = w[u];
return;
}
bool first = 1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v == p)
continue;
dfs(v, u);
if(first) {
dp[u][0] = w[u] + max(dp[v][k], dp[v][k + 1]);
for(int j = 0; j <= k - 1; ++j)
dp[u][j + 1] = dp[v][j];
dp[u][k + 1] = max(dp[v][k], dp[v][k + 1]);
first = 0;
} else {
tmp[0] = dp[u][0] + max(dp[v][k], dp[v][k + 1]);
for(int j = 0; j <= k - 1; ++j)
tmp[j + 1] = dp[v][j];
tmp[k + 1] = max(dp[v][k], dp[v][k + 1]);
for(int j = 0; j <= k + 1; ++j) {
for(int t = max(0, k - j); t <= k + 1; ++t)
tmp[min(j + 1, t)] = max(tmp[min(j + 1, t)], dp[u][t] + dp[v][j]);
}
for(int j = 0; j <= k + 1; ++j)
dp[u][j] = max(dp[u][j], tmp[j]);
}
}
/*for(int j = 0; j <= k; ++j)
printf("dp[%d][%d]=%d\n", u, j, dp[u][j]);
puts("");*/
}
void test_case() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &w[i]);
if(n == 1) {
printf("%d\n", w[1]);
return;
}
for(int i = 1; i <= n - 1; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, -1);
int ans = 0;
for(int j = 0; j <= k + 1; ++j)
ans = max(ans, dp[1][j]);
printf("%d\n", ans);
}
不要漏掉了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)的更多相关文章
- Codeforces Round #595 (Div. 3)D1D2 贪心 STL
一道用STL的贪心,正好可以用来学习使用STL库 题目大意:给出n条可以内含,相交,分离的线段,如果重叠条数超过k次则为坏点,n,k<2e5 所以我们贪心的想我们从左往右遍历,如果重合部分条数超 ...
- Codeforces Round #595 (Div. 3)B2 简单的dfs
原题 https://codeforces.com/contest/1249/problem/B2 这道题一开始给的数组相当于地图的路标,我们只需对每个没走过的点进行dfs即可 #include &l ...
- Codeforces Round #595 (Div. 3) D2Too Many Segments,线段树
题意:给n个线段,每个线段会覆盖一些点,求删最少的线段,使得每个点覆盖的线段不超过k条. 思路:按右端点排序,之后依次加入每个线段,查询线段覆盖区间内的每个点,覆盖的最大线段数量,如果不超过k,那就可 ...
- Codeforces Round #595 (Div. 3) A,B,C,D
A题:n个学生,分成任意组,要求每组中任意两名学生的技能值相差不等于1,问最小分组. #include<bits/stdc++.h> using namespace std; #defin ...
- Codeforces Round #595 (Div. 3) 题解
前言 大家都在洛谷上去找原题吧,洛谷还是不错的qwq A 因为没有重复的数,我们只要将数据排序,比较两两之间有没有\(a_j - a_i == 1 (j > i)\) 的,有则输出 \(2\) ...
- 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 ...
- Codeforces Round #354 (Div. 2) ABCD
Codeforces Round #354 (Div. 2) Problems # Name A Nicholas and Permutation standard input/out ...
- Codeforces Round #368 (Div. 2)
直达–>Codeforces Round #368 (Div. 2) A Brain’s Photos 给你一个NxM的矩阵,一个字母代表一种颜色,如果有”C”,”M”,”Y”三种中任意一种就输 ...
- cf之路,1,Codeforces Round #345 (Div. 2)
cf之路,1,Codeforces Round #345 (Div. 2) ps:昨天第一次参加cf比赛,比赛之前为了熟悉下cf比赛题目的难度.所以做了round#345连试试水的深浅..... ...
随机推荐
- [golang]图片按中心旋转后,新图的左顶点位置的偏移量
1 前言 图片按中心旋转后,新图的左顶点位置的偏移量 2 代码 func OffsetXYAfterRotationCore(W, H, L, T, Angle float64) (x, y floa ...
- Angular使用操作事件指令ng-click传多个参数示例
本文实例讲述了Angular使用操作事件指令ng-click传多个参数功能.分享给大家供大家参考,具体如下: <!DOCTYPE html> <html ng-app="m ...
- 【转载】C#中Convert.ToInt32方法将字符串转换为Int32类型
在C#编程过程中,可以使用Convert.ToInt32方法将字符串或者其他可转换为数字的对象变量转换为ToInt32类型,Convert.ToInt32方法有多个重载方法,最常使用的一个方法将字符串 ...
- Oracle 逻辑存储结构
一.总述 逻辑存储结构是 Oracle 数据库存储结构的核心内容,对 Oracle 数据库的所有操作都会涉及逻辑存储结构.逻辑存储结构是从逻辑的角度分析数据库的组成,是对数据存储结构在逻辑概念上的划分 ...
- 如何自行给指定的SAP OData服务添加自定义日志记录功能
有的时候,SAP标准的OData实现或者相关的工具没有提供我们想记录的日志功能,此时可以利用SAP系统强大的扩展特性,进行自定义日志功能的二次开发. 以SAP CRM Fiori应用"My ...
- Windows Server 2008 R2 + IIS 环境部署Asp.Net Core App
Windows + IIS 环境部署Asp.Net Core App 环境:Windows Server 2012, IIS 8, Asp.Net Core 1.1. 不少人第一次在IIS中部署A ...
- css样式设定样例说明
<style> .classA .classB{*}; //表示设置class为classA标签下的classB标签的样式(A). .classA , .classB{*}; //表示同时 ...
- centos7.6初始化python3.6环境
环境: CentOS Linux release 7.6.1810 (Core) Python3.6.x 01.检测yum源 wget -O /etc/yum.repos.d/epel.repo ht ...
- 解决maven install报错:java.lang.NoClassDefFoundError: org/codehaus/plexus/compiler/util/scan/InclusionScanException
问题:maven install时,报错:java.lang.NoClassDefFoundError: org/codehaus/plexus/compiler/util/scan/Inclusio ...
- 数据库开发-Django ORM的一对多查询
数据库开发-Django ORM的一对多查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.联合主键问题 CREATE TABLE `employees` ( `emp_no` ...