初见安~这里是传送门:洛谷P3105 Fair Photography
题解
这个题很有意思呀——因为白牛可以染色成花牛,所以我们选择的区间内白牛的数量减去花牛的数量必须是非负偶数。很明显我们可以差分一下——白牛值为1,花牛值为-1,求出前缀和。因为不管当前牛是白的还是花的,其前缀和的奇偶性都一定会发生改变,所以假设我们所求区间为,如果我们要求为非负偶数,换言之就是要求为非负奇数。
我们枚举区间右端点,将当前端点前面的节点按照奇偶划分进两个数组。明显要找的左端点是在与右端点的奇偶性相反的那个数组。为了让区间尽量大,我们要找尽量靠左的区间左端点。所以进一步的问题就是——我们要在一个数列里面找一个尽量靠前的 值小于等于当前点的前缀和 的数。实测从前往后挨个扫会TLE 4个点,复杂度近似。所以我们考虑二分查找。
这里同样有个小技巧——我们再整一个前缀最小值,然后在这个最小值上二分。也就是说:我们二分一个中间位置mid,因为要尽量靠左【也就是靠前】,如果mid左边的最小值小于等于当前值,也就是说再往左走会有更优的解,我们就往左走;反之往右走。边界无解的问题稍作处理即可。
然后这个题就差不多啦。数组开下来会有点儿多,看起来很繁琐所以建议不要深入看代码呢。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 100005
using namespace std;
typedef long long ll;
const int INF = 1e9;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
struct node {
int p, kind, num;
bool operator < (const node &x) const {return p < x.p;}
}a[maxn];
int n, sum[maxn];
int stc[2][maxn], top[2];//两个stc分开放奇偶的数的下标
int minn[2][maxn];//前缀最小值
int find(int lim, int kd) {//二分
register int l = 0, r = top[kd], mid, ans = 0;
while(l <= r) {
mid = l + r >> 1;
if(minn[kd][mid] <= lim) ans = mid, r = mid - 1;
else l = mid + 1;
}
return a[stc[kd][ans]].p;
}
signed main() {
n = read();
for(int i = 1; i <= n; i++) {
a[i].p = read();
char op; cin >> op;
if(op == 'W') a[i].kind = 1; else a[i].kind = -1;
}
sort(a + 1, a + 1 + n);
a[0].num = n; a[0].p = INF; top[0] = top[1] = 0;
minn[0][0] = minn[1][0] = INF;
int ans = 0;
for(int i = 1; i <= n; i++) {
a[i].num = a[i - 1].num + a[i].kind;
int kd = (a[i].num & 1);
ans = max(ans, a[i].p - find(a[i].num, kd ^ 1));
stc[kd][++top[kd]] = i, minn[kd][top[kd]] = min(minn[kd][top[kd] - 1], a[i].num);
}
printf("%d\n", ans);
return 0;
}//197178666
迎评:)
——End——