A. Meeting of Old Friends(Codeforces 714A)
思路
首先我们可以讨论两个区间的位置关系(内含,相交,相等或相离),相离情况下答案为
0
,不相离的情况下求出这两个区间的公共部分,然后判断那个间断时间点在不在公共部分就能求出结果了。
比较巧妙的办法是,令
(以下的代码用的是直接分类讨论的方法)
代码
#include <bits/stdc++.h>
using namespace std;
long long l1, r1, l2, r2, k, L, R;
int main() {
cin >> l1 >> r1 >> l2 >> r2 >> k;、
// 区间相离
if(r1 < l2 || r2 < l1) {
cout << 0;
}
// 区间相等或区间2包含区间1
else if(l1 >= l2 && r1 <= r2) {
cout << r1 - l1 + (l1 > k || k > r1);
}
// 区间相交
else if(l2 > l1 && r2 < r1) {
cout << r2 - l2 + (l2 > k || k > r2);
}
// 区间相交
else {
L = max(l1, l2);
R = min(r1, r2);
cout << R - L + (L > k || k > R);
}
return 0;
}
B. Filya and Homework(Codeforces 714B)
思路
我们先统计数组中不同数的个数是多少(假设它为
m
)。然后可以根据
- m=1 或 m=2 。此时一定能让所有的数相等。
- m=3 。若最大的数减第二大的数等于第二大的数减去最小的数则能够让所有数相等,否则不能。
- m≥4 。此时一定无法让所有的数相等。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, m, a[maxn];
int main() {
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
sort(a, a + n);
m = unique(a, a + n) - a;
if(m >= 4) {
puts("NO");
}
else if(m == 1 || m == 2) {
puts("YES");
}
else {
puts(a[2] - a[1] == a[1] - a[0] ? "YES" : "NO");
}
return 0;
}
C.Sonya and Queries(Codeforces 714C)
思路
首先,我们可以将输入的数字当成
0−1
字符串,其中奇数对应的位为
1
,否则为
然后,每读入一个操作符为
+
或
当操作符为
?
时,在
(赛后发现有更简单的方法,就是直接将
0−1
字符串压缩成整数
k
,用
代码
#include <bits/stdc++.h>
using namespace std;
const int maxNode = 2e6, sigmaSize = 5;
// Trie的模板
struct Trie {
int ch[maxNode][sigmaSize];
int val[maxNode];
int sz;
Trie() {
sz = 1;
memset(ch[0], 0, sizeof(ch[0]));
}
int idx(char c) {
return c - '0';
}
void insert(string s, int v) {
int u = 0, n = s.size();
for(int i = 0; i < n; i++) {
int c = idx(s[i]);
if(!ch[u][c]) {
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] += v;
}
int find(string s) {
int u = 0, n = s.size();
for(int i = 0; i < n; i++) {
int c = idx(s[i]);
if(!ch[u][c]) {
return 0;
}
u = ch[u][c];
}
return val[u];
}
}o;
int n;
string s, t, opt;
int main() {
ios_base::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> opt >> s;
// 处理成0-1字符串
for(int i = 0; i < s.size(); i++) {
s[i] = (s[i] - '0') % 2 ? '1' : '0';
}
string t;
// 在首部添0
for(int i = 1; i <= 18 - s.size(); i++) {
t.push_back('0');
}
s = t + s;
if(opt != "?") {
o.insert(s, opt == "+" ? 1 : -1);
}
else {
cout << o.find(s) << endl;
}
}
return 0;
}
E. Sonya and Problem Wihtout a Legend(Codeforces 714E)
思路
先来思考一个简单的问题。当题目中的“严格单调递增”改成“单调非递减”时,问题该怎样解决。
用搜索解决肯定是复杂度太高了,如果用动态规划呢?我们先尝试划分阶段。假设我们定义阶段i为考虑到第
于是我们定义
d[i][j]
为当考虑到第
i
个数字且第
d[i][j]=min{d[i−1][k]+a[i]−j,j≥k}
现在似乎按照方程递推就能得解了。但是此处还有两个问题需要解决。第一,
O(n3)
的复杂度还是太高了。这个问题可以通过建立前缀数组来解决,具体就是令
Min[j]为mind[i−1][k],j≥k
。这样方程就变成了
d[i][j]=Min[j]+a[i]−j
,复杂度为
O(n2)
。第二,
a[i]
的上界为
109
,那么
d[i][j]
将会很大。这个问题可以通过将数组
a
离散化来解决。这样空间复杂度也被优化到
这样以来整个问题就解决了。但是原问题似乎还没得到解决。事实上,我们可以简单地令所有的
a[i]=a[i]−i
。就能将问题转化成我们刚刚讨论的问题,这样原问题也顺利得解了。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3010;
int n, m, a[maxn], b[maxn];
ll ans, Min[maxn], d[maxn][maxn];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
a[i] = a[i] - i;
b[i] = a[i];
}
// 离散化中的排序
sort(b + 1, b + n + 1);
// 离散化中的去重
m = unique(b + 1, b + n + 1) - b - 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
d[i][j] = Min[j] + abs(b[j] - a[i]);
}
Min[1] = d[i][1];
// 处理前缀最小值数组
for(int j = 2; j <= m; j++) {
Min[j] = min(d[i][j], Min[j-1]);
}
}
printf("%I64d\n", Min[m]);
return 0;
}
(其它题目略)