A - Math Problem
题意:给n条线段[l,r],求再加一条可以退化成点的线段,与所有线段各至少有一个公共点。
题解:求出最右边的左端点和最左边的右端点,左端点>右端点,则说明你的这个线段要把这两个连起来,否则你的线段就是现在的[左端点,右端点]上的随便一个点。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void test_case() {
int n;
scanf("%d", &n);
int L = 1, R = 1e9;
for(int i = 1; i <= n; ++i) {
int l, r;
scanf("%d%d", &l, &r);
L = max(L, l);
R = min(R, r);
}
int ans = 0;
if(L > R)
ans = L - R;
printf("%d\n", ans);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
B - Box
题意:给一个前缀最大值序列,求满足这个序列的任意一个排列。
题解:生成的是满足这个序列的字典序最大的一个排列。而实际上因为这个序列是递增的,怎么乱构造都可以。首先每次最大值变化的位置肯定放这个罪魁祸首,然后扫一遍之后可能会有一些位置没有放,这个时候lower_bound其最大值得到大于等于其最大值的任意一个值。然后--之后就是小于它的最大的一个值,把它放过去就可以了。或者每次就放begin迭代器,直到begin迭代器超过最大值就-1,这样是字典序最小。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int q[100005];
int p[100005];
set<int> tmp;
void test_case() {
int n;
scanf("%d", &n);
tmp.clear();
for(int i = 1; i <= n; ++i) {
p[i] = -1;
tmp.insert(i);
}
for(int i = 1; i <= n; ++i)
scanf("%d", &q[i]);
p[1] = q[1];
tmp.erase(q[1]);
for(int i = 2; i <= n; ++i) {
if(q[i] > q[i - 1]) {
p[i] = q[i];
tmp.erase(q[i]);
}
}
for(int i = 1; i <= n; ++i) {
if(p[i] == -1) {
auto t = tmp.lower_bound(q[i]);
if(t == tmp.begin()) {
puts("-1");
return;
}
--t;
p[i] = *t;
tmp.erase(t);
}
}
for(int i = 1; i <= n; ++i)
printf("%d%c", p[i], " \n"[i == n]);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
C - Messy
题意:给一个"("")"括号相等的长度不超过2000的括号串,要求每次反转一个子区间,最后使得括号串合法,且经过零点恰好k次。
题解:贪心,每次贪心把前两个字符变成"("")",这样最后就有n/2次零点,然后把开头开始的一小段破坏掉就可以了(假如需要的话,不要画蛇添足),至多用n/2+1次。因为网络的原因没有交上去,问题不大,实力到了自然会收敛。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, k;
string s;
vector<pair<int, int> > ans;
void test_case() {
ans.clear();
cin >> n >> k >> s;
int curp = 1;
while(s.length()) {
if(s[0] == '(' && s[1] == ')') {
s = s.substr(2, s.length());
curp += 2;
} else if(s[0] == ')' && s[1] == '(') {
ans.push_back({curp + 0, curp + 1});
s = s.substr(2, s.length());
curp += 2;
} else if(s[0] == ')') {
int j = 2;
while(s[j] == s[0])
++j;
ans.push_back({curp + 0, curp + j});
string s1 = s.substr(0, j + 1);
string s2 = s.substr(j + 1, s.length());
reverse(s1.begin(), s1.end());
s = s1 + s2;
s = s.substr(2, s.length());
curp += 2;
} else {
int j = 2;
while(s[j] == s[0])
++j;
ans.push_back({curp + 1, curp + j});
string s0 = s.substr(0, 1);
string s1 = s.substr(1, j);
string s2 = s.substr(j + 1, s.length());
reverse(s1.begin(), s1.end());
s = s0 + s1 + s2;
s = s.substr(2, s.length());
curp += 2;
}
}
int tk = n / 2 - k;
if(1 + 2 * tk >= 2)
ans.push_back({2, 1 + 2 * tk});
cout << ans.size() << endl;
for(int i = 0; i < ans.size(); ++i)
cout << ans[i].first << " " << ans[i].second << endl;
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
注:要注意string的substr方法的使用,真的烦死人,substr(a,b)是从a位置开始截取最多b个字符形成的子串。其实假如用静态字符数组会不会方便一些?要是自己写一个String类就可以使用自己的全新方法了。