传送门

A. Paint the Numbers

签到。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105;
int n;
int a[N];
bool used[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + n + 1);
int ans = 0;
for(int i = 1; i <= n; i++) {
if(used[i]) continue;
++ans;
for(int j = i; j <= n; j++) {
if(a[j] % a[i] == 0) used[j] = 1;
}
}
cout << ans;
return 0;
}

B. Koala and Lights

由于数据范围很小,暴力枚举即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105; int n;
int a[N], b[N];
char s[N]; int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
cin >> s + 1;
for(int i = 1; i <= n; i++) cin >> a[i] >> b[i];
int ans = 0;
for(int i = 1; i <= n; i++) {
if(s[i] == '1') ++ans;
}
for(int i = 1; i <= 5000; i++) {
int tmp = 0;
for(int j = 1; j <= n; j++) {
if(i - b[j] < 0) continue;
int now = i - b[j];
if(now % a[j] == 0) {
if(s[j] == '1') s[j] = '0';
else if(s[j] == '0') {
s[j] = '1';
}
}
}
for(int j = 1; j <= n; j++) {
if(s[j] == '1') ++tmp;
}
// if(i < 10) cout << tmp << '\n';
ans = max(ans, tmp);
}
cout << ans;
return 0;
}

C. Paint the Digits

题意:

给出一串数字,现在要给这串数字涂上两种颜色\(1,2\),要求涂色过后从前往后将\(1\)排开,再将\(2\)排开,最终得到一个非递减序列。

思路:

首先序列自动机预处理下一位,然后贪心处理即可。

感性想一下很容易知道染色方案,对于当前位,\(1\)的颜色肯定是下一个最小的值,如果不是则不满足条件;\(1\)染完色过后按同样的方法对\(2\)进行染色,但是起点不同,注意处理一下。

还有一个细节:\(1\)染的颜色的最大值不能超过前面没染颜色的最小值,否则也不满足条件。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5; int T, n;
char s[N];
int nxt[N][26];
int last[26];
int col[N]; int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> T;
while(T--) {
cin >> n;
cin >> s + 1;
for(int i = 1; i <= n; i++) col[i] = 0;
for(int i = 0; i < 26; i++) last[i] = n + 1;
for(int i = n; i >= 0; i--) {
for(int j = 0; j < 26; j++) nxt[i][j] = last[j];
if(i) last[s[i] - '0'] = i;
}
int start = 0, up = 26;
for(int i = 0, k; i <= n; i = k) {
int jump = 0;
int low = (i == 0 ? 0 : s[i] - '0');
for(int j = low; j < up; j++) {
if(nxt[i][j] <= n) {
k = nxt[i][j];
jump = 1;
break;
}
}
if(jump == 0) break;
col[k] = 1;
start = s[k] - '0';
if(k != i + 1 && up == 26) {
up = s[i + 1] - '0' + 1;
}
}
for(int i = 0, k; i <= n; i = k) {
int jump = 0;
int low = (i == 0 ? start : s[i] - '0');
for(int j = low; j < 26; j++) {
if(nxt[i][j] <= n && !col[nxt[i][j]]) {
k = nxt[i][j];
jump = 1;
break;
}
}
if(!jump) break;
col[k] = 2;
}
int ok = 1;
for(int i = 1; i <= n; i++) {
if(!col[i]) ok = 0;
}
if(!ok) cout << '-' << '\n';
else {
for(int i = 1; i <= n; i++) cout << col[i];
cout << '\n';
}
}
return 0;
}

D. Cow and Snacks

全局视野来看,并查集搞搞就行,答案就与生成树有关。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5; int n, k;
int a[N], b[N];
int f[N], sz[N]; int find(int x) {
return f[x] == x ? f[x] : f[x] = find(f[x]);
} bool Union(int x, int y) {
int fx = find(x), fy = find(y);
if(fx != fy) {
sz[fy] += sz[fx];
f[fx] = fy;
return true;
}
return false;
} int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> k;
for(int i = 1; i <= k; i++) {
cin >> a[i] >> b[i];
}
for(int i = 1; i <= n; i++) f[i] = i, sz[i] = 1;
int ans = 0;
for(int i = 1; i <= k; i++) {
if(!Union(a[i], b[i])) ++ans;
}
// int cnt = 0;
// for(int i = 1; i <= n; i++) {
// if(find(i) == i) {
// cnt += sz[i] - 1;
// cout << sz[i] << '\n';
// }
// }
// int ans = n - cnt;
cout << ans;
return 0;
}

E. Rotate Columns

题意:

给出一个\(n*m\)的矩形,满足\(n\leq 12,m\leq 2000\),现在你可以对任意一列执行任意次滑动操作。

记\(r_i\)为第\(i\)行的最大值,求\(\sum_{i=1}^nr_i\)最大为多少。

思路:

一开始看作行也可以滑动,然后就写了一个假题= =

  • 有一个很重要的观察,就是最后的最大值只与最大值最大的\(n\)列有关。
  • 为什么?
  • 假设现在第\(i\)列的最大值为第\(n+1\)大,那么在最优方案中,它肯定不能成为最大值,因为总会存在一列\(j\)可以令其滑动到\(i\)的位置并且其最大值大于\(j\)列。
  • 那么我们将最大的\(n\)列拿出来随机就行了。
  • 因为数据很小,所以可以考虑状压。
  • 枚举状态\(s\),表示在之前的\(i-1\)列中选取某些行作为最大值,和的最大值。之后会新加进来第\(i\)列,显然,第\(i\)列行的状态只能为\(((1<<n)-1)\ xor\ s\)的子集。之后枚举循环位移更新即可。
  • 直接枚举循环位移时间复杂度可能会多处一个\(O(n)\),那么可以将这部分预处理一下。

一开始我卡在枚举循环位移这里,后面想了下,只有枚举循环位移,才会覆盖到所有情况。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define INF 0x3f3f3f3f
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 12, M = 2000; int n, m;
int a[N][M];
pii b[M]; void run() {
cin >> n >> m;
vector <int> col(m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
col[j] = max(col[j], a[i][j]);
}
}
for (int i = 0; i < m; i++) b[i] = MP(col[i], i);
sort(b, b + m); reverse(b, b + m);
m = min(m, n);
int lim = 1 << n;
vector <vector<int>> w(m, vector<int>(lim));
for (int i = 0; i < m; i++) {
int j = b[i].se;
for (int s = 0; s < lim; s++) {
for (int k = 0; k < n; k++) {
int sum = 0;
for (int p = 0; p < n; p++) {
if (s >> ((p + k) % n) & 1) {
sum += a[p][j];
}
}
w[i][s] = max(w[i][s], sum);
}
}
}
vector <int> dp(lim);
for (int i = 0; i < m; i++) {
vector <int> new_dp(lim);
for (int s = 0; s < lim; s++) {
int other = (lim - 1) ^ s;
for (int sub = other; ; sub = (sub - 1) & other) {
int Max = w[i][sub];
new_dp[s ^ sub] = max(new_dp[s ^ sub], dp[s] + Max);
if (sub == 0) break;
}
}
swap(dp, new_dp);
}
cout << dp[lim - 1] << '\n';
} int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
int t; cin >> t;
while(t--) run();
return 0;
}

F. Koala and Notebook

题意:

给出一张\(n\)个点\(m\)条边的无向图,每条边的编号依次为\(1,2,\cdots,m\)。

现在从\(1\)号结点出发,每经过一条边,则在目前答案后面连上当前边的编号。

比如\(1->2\),编号为\(10\),到达\(2\)结点的结果就为\(10\);之后\(2->3\)的编号为\(11\),那么结果就为\(1011\)。

现在要求到达每个节点的最小答案,由于这个答案可能很大,所以输出对\(1e9+7\)取模的答案。

思路:

一开始想不是很好想,因为思考起来还是挺麻烦的,比较大小则要考虑很多东西。

但其实细化一下思路就能清晰一点:

  • 一般比较字典序大小,第一看长度,第二看不同位的大小关系。
  • 但这里长度不是很好比较,不同编号可能有不同的长度。怎么处理?
  • 拆边就行!将编号拆成多个一位,这样长度就很好考虑。
  • 接下来考虑大小关系。
  • 因为我们从\(1\)出发,所以可以跑个\(bfs\),每次往小的边优先走,那么既能保证长度最小,也能保证值最小了。

大概思路就是这样,关键点就是拆边。

但代码实现还有一点细节:我们\(bfs\)时用\(vector\)来模拟队列,之后再将距离拆细一点(即并不是距离离起点相同的点都拿出来更新),这样就能保证每次更新得到的都是最小答案。不然对于\(0\)通过\(1\)去更新其它点,还不如\(1\)通过\(0\)去更新其它点优,就有问题。

说这么多废话,代码看起来还是比较好懂:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 5, MOD = 1e9 + 7; int n, m;
vector <int> g[N][10];
bool chk[N];
int ans[N]; void run() {
int np = n;
for(int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
int t = i;
vector <int> e;
while(t) {
e.push_back(t % 10);
t /= 10;
}
reverse(all(e));
int pre = u;
for(int j = 0; j < sz(e); j++) {
int to = (j == sz(e) - 1 ? v : ++np);
g[pre][e[j]].push_back(to);
pre = to;
}
pre = v;
for(int j = 0; j < sz(e); j++) {
int to = (j == sz(e) - 1 ? u : ++np);
g[pre][e[j]].push_back(to);
pre = to;
}
}
vector <vector<int>> q(np + 1);
chk[1] = 1; q[0].push_back(1);
int T = 0;
for(int i = 0; i <= T; i++) {
for(int j = 0; j < 10; j++) {
int f = 0;
for(auto x : q[i]) {
for(auto it : g[x][j]) {
if(!chk[it]) {
chk[it] = 1; f = 1;
ans[it] = (1ll * 10 * ans[x] % MOD + j) % MOD;
q[T + 1].push_back(it);
}
}
}
if(f) ++T;
}
}
for(int i = 2; i <= n; i++) cout << ans[i] << '\n';
} int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
while(cin >> n >> m) run();
return 0;
}

G1. Into Blocks (easy version)

题意:

给出一个长度为\(n,n\leq 2e5\)的序列。

现在可以执行操作:将全部的\(x\)变为\(y\)。

现在问最少多少次操作,使得所有值相同的元素都在同一块中。

思路:

  • 考虑最终状态,一定为多个整块,并且在一个整块中,一定是多个颜色都变为同一颜色。
  • 那么对于\(1\)位置的颜色,一定最后也在一块中,并且目前块结尾为\(last[a_1]\);同时,中间的所有元素都会变为同一颜色,那么我们直接扫过去,因为可能\(last[a_i]>last[a_1]\),我们直接更新右边界即可。
  • 最后我们划分出了多个大区间,在每个区间中贪心考虑保留个数最多的颜色即可。那么我们划分时用一个桶维护一下权值数量即可。

详见代码:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200005; int n, q;
int a[N];
int fir[N], last[N];
int cnt[N]; int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) {
if(fir[a[i]] == 0) fir[a[i]] = i;
}
for(int i = n; i >= 1; i--) {
if(last[a[i]] == 0) last[a[i]] = i;
}
int ans = 0;
for(int i = 1, j; i <= n; i = j + 1) {
j = last[a[i]];
int st = i;
while(i < j) {
++i; j = max(last[a[i]], j);
}
int tmp = 0;
for(int k = st; k <= j; k++) {
if(++cnt[a[k]] > tmp) tmp = cnt[a[k]];
}
ans += j - st + 1 - tmp;
}
cout << ans;
return 0;
}

Codeforces Round #584的更多相关文章

  1. Codeforces Round #584 E2. Rotate Columns (hard version)

    链接: https://codeforces.com/contest/1209/problem/E2 题意: This is a harder version of the problem. The ...

  2. Codeforces Round #584 D. Cow and Snacks

    链接: https://codeforces.com/contest/1209/problem/D 题意: The legendary Farmer John is throwing a huge p ...

  3. Codeforces Round #584 C. Paint the Digits

    链接: https://codeforces.com/contest/1209/problem/C 题意: You are given a sequence of n digits d1d2-dn. ...

  4. Codeforces Round #584 B. Koala and Lights

    链接: https://codeforces.com/contest/1209/problem/B 题意: It is a holiday season, and Koala is decoratin ...

  5. Codeforces Round #584 A. Paint the Numbers

    链接: https://codeforces.com/contest/1209/problem/A 题意: You are given a sequence of integers a1,a2,-,a ...

  6. Codeforces Round 584 题解

    -- A,B 先秒切,C 是个毒瘤细节题,浪费了很多时间后终于过了. D 本来是个 sb 题,然而还是想了很久,不过幸好没什么大事. E1,看了一会就会了,然而被细节坑死,好久才过. 感觉 E2 很可 ...

  7. Codeforces Round #584 (Div. 1 + Div. 2)

    Contest Page A sol 每次选最小的,然后把它的所有倍数都删掉. #include<bits/stdc++.h> using namespace std; int read( ...

  8. 状压DP--Rotate Columns (hard version)-- Codeforces Round #584 - Dasha Code Championship - Elimination Round (rated, open for everyone, Div. 1 + Div. 2)

    题意:https://codeforc.es/problemset/problem/1209/E2 给你一个n(1-12)行m(1-2000)列的矩阵,每一列都可以上下循环移动(类似密码锁). 问你移 ...

  9. Cow and Snacks(吃点心--图论转换) Codeforces Round #584 - Dasha Code Championship - Elimination Round (rated, open for everyone, Div. 1 + Div. 2)

    题意:https://codeforc.es/contest/1209/problem/D 有n个点心,有k个人,每个人都有喜欢的两个点心,现在给他们排个队,一个一个吃,每个人只要有自己喜欢的点心就会 ...

随机推荐

  1. 数据安全管理:RSA加密算法,签名验签流程详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.RSA算法简介 1.加密解密 RSA加密是一种非对称加密,在公开密钥加密和电子商业中RSA被广泛使用.可以在不直接传递密钥的情况下,完成加 ...

  2. C#线程学习笔记二:线程池中的工作者线程

    本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/ThreadPool.html,记录一下学习过程以备后续查用. 一.线程池基础 首先,创 ...

  3. 松软科技web课堂:JavaScript Math 对象

    JavaScript Math 对象允许您对数字执行数学任务. 实例 Math.PI; // 返回 3.141592653589793 Math.round() Math.round(x) 的返回值是 ...

  4. iOS----------componentsJoinedByString 和 componentsSeparatedByString 的方法的区别

    将string字符串转换为array数组 NSArray  *array = [Str componentsSeparatedByString:@","]; ==反向方法 将arr ...

  5. 《精通Python爬虫框架Scrapy》学习资料

    <精通Python爬虫框架Scrapy>学习资料 百度网盘:https://pan.baidu.com/s/1ACOYulLLpp9J7Q7src2rVA

  6. Python内置装饰器@property

    在<Python装饰器(Decorators )>一文中介绍了python装饰器的概念,日常写代码时有一个装饰器很常见,他就是内置的@property. 我们一步步的来接近这个概念. 一个 ...

  7. centeros系统之上传下载文件

    安装lrzszlrzsz这个软件,可以让我们直接从linux上,下载和上传文件的操作 yum install -y lrzsz11.上传文件通过输入 rz命令,可以弹出上传文件的对话框,然后就可以上传 ...

  8. 《HTTPS权威指南》读书笔记——PKI

    互联网公钥基础设施 基于可信的第三方机构(CA,certification authority)实现不同成员在不见面的情况下进行安全通信 订阅人 需要证书来提供安全服务的团体 登记机构(RA) 完成证 ...

  9. nginx(4)

    目录 一.安装配置 1.安装 2.配置文件 3.测试和启动 二.功能 1.虚拟主机 1.1 基于IP 1.2 基于域名 1.3 基于端口 2.访问控制 3.用户认证 4.文件共享 5.文件别名 6.状 ...

  10. LeetCode 第70题动态规划算法

    导言 看了 动态规划(https://www.cnblogs.com/fivestudy/p/11855853.html)的帖子,觉得写的很好,记录下来. 动态规划问题一直是算法面试当中的重点和难点, ...