题目链接
A - Azulejos
瓷砖有价格和高度两个属性,求两排瓷砖的一个排列,使得每一排瓷砖的价格从左到右非递减,每个位置上后排的高度严格大于前排的高度。
很容易想到基本思路就是贪心,对于前排的瓷砖,要和后排中高度高于它且高度最小的瓷砖匹配。关键就是要确定匹配的顺序,使得匹配后前后两排的价格都是非递减的。
这里先将瓷砖分别按照价格升序排序,每次维护出价格相同的区间,优先给元素少的一边匹配,用set去查找,其中任意一次找不到结果即为无解。因为要求是价格非递减,所以在已经取出的元素里查找不到就是无解。时间复杂度。
以及set查找的时候,std::lower_bound由于set<>::iterator不支持随机访问,单次查找的复杂度显然是超过了,所以一定要用std::set::lower_bound……
#include
using namespace std;
const int MAXN = 5e5 + 7;
int n, ansB[MAXN], ansF[MAXN];
struct node {
int price, height;
int id;
bool operator < (const node &o) {
return price < o.price;
}
}B[MAXN], F[MAXN];
set< pair > setB, setF;
set< pair > :: iterator itB, itF;
int main() {
cin >> n;
for(int i = 0;i < n;i++) {
cin >> B[i].price;
B[i].id = i + 1;
}
for(int i = 0;i < n;i++) {
cin >> B[i].height;
}
for(int i = 0;i < n;i++) {
cin >> F[i].price;
F[i].id = i + 1;
}
for(int i = 0;i < n;i++) {
cin >> F[i].height;
}
sort(B, B+n), sort(F, F+n);
int posB = 0, posF = 0;
for(int i = 0;i < n;i++) {
if(setB.empty()) {
while(posB < n) {
setB.insert(make_pair(B[posB].height, B[posB].id));
if(B[++posB].price != B[posB-1].price) break;
}
}
if(setF.empty()) {
while(posF < n) {
setF.insert(make_pair(F[posF].height, F[posF].id));
if(F[++posF].price != F[posF-1].price) break;
}
}
if(setB.size() < setF.size()) {
int nowH = setB.begin() -> first;
int nowB = setB.begin() -> second;
itF = setF.lower_bound(make_pair(nowH, -1));
//itF = lower_bound(setF.begin(), setF.end(), make_pair(nowH, -1));
if(itF == setF.begin()) {
return (cout << "impossible" << endl, 0);
}
ansB[i] = nowB, ansF[i] = (--itF) -> second;
setB.erase(setB.begin()), setF.erase(itF);
}
else {
int nowH = setF.begin() -> first;
int nowF = setF.begin() -> second;
itB = setB.upper_bound(make_pair(nowH, n+1));
//itB = upper_bound(setB.begin(), setB.end(), make_pair(nowH, n+1));
if(itB == setB.end()) {
return (cout << "impossible" << endl, 0);
}
ansF[i] = nowF, ansB[i] = itB -> second;
setF.erase(setF.begin()), setB.erase(itB);
}
}
for(int i = 0;i < n;i++) {
cout << ansB[i] << (i == n-1 ? '\n' : ' ');
}
for(int i = 0;i < n;i++) {
cout << ansF[i] << (i == n-1 ? '\n' : ' ');
}
return 0;
}
D - Circular DNA
给出一个环状序列,要求切开后使同类元素组成的字串为合法的括号序列,需使得满足此要求的元素种类最多。求切开的位置及满足条件的元素种类。
对于某一种元素,如果它的s和e的数量不同,那么无论如何切割, 这种元素必不可能满足条件,可以直接跳过。顺时针旋转地去遍历它的所有位置,如果令s的贡献为1、e的贡献为-1,处理出各位置贡献的前缀和,那么当且仅当在前缀和取最小值的位置切开时,可以使这类元素满足题设要求。也就是说,将切割点放在最小值与最小值的下一个位置之间时,它在这个区间上对于总的种类数有着1的贡献。
这样,就变成了一个区间更新查询最大值的问题,用差分的方法就可以解决。
#include
using namespace std;
const int MAXN = 1e6 + 7;
int n, a[MAXN], ans[MAXN];
int pos[MAXN], bin[MAXN], sum[MAXN];
map< int, set > mp;
int read() {
int val = 0, k;
char ch = getchar();
while (ch != 's' && ch != 'e') {
ch = getchar();
}
k = (ch == 's' ? 1 : -1);
ch = getchar();
while (ch >= '0' && ch <= '9') {
val *= 10;
val += (ch - '0');
ch = getchar();
}
return val * k;
}
int main() {
scanf("%d", &n);
for (int i = 1;i <= n;i++) {
a[i] = read();
mp[abs(a[i])].insert(i);
}
for (auto it : mp) {
int cnt = 0, minn = 0;
for (auto now : mp[it.first])
{
pos[++cnt] = now;
bin[cnt] = (a[now] > 0 ? 1 : -1);
sum[cnt] = sum[cnt - 1] + bin[cnt];
minn = min(minn, sum[cnt]);
}
if(sum[cnt] != 0) continue;
for (int i = 1; i <= cnt;i++) {
if(sum[i] == minn) {
int st = pos[i] % n + 1, en = pos[i % cnt + 1];
ans[st]++, ans[en + 1]--;
if(st > en) ans[1]++;
}
}
}
int maxx = -1, res;
for (int i = 1;i <= n;i++) {
ans[i] += ans[i - 1];
if(ans[i] > maxx) {
maxx = ans[i];
res = i;
}
}
printf("%d %d\n", res, maxx);
return 0;
}