【NOIP2019模拟11.01】Game(贪心+线段树)

Description:

​ 小 A 和小 B 在玩一个游戏,他们两个人每人有 张牌,每张牌有一个点数,并且在接下来的 个回合中每回合他们两人会分别打出手中的一张牌,点数严格更高的一方得一分,然而现在小 A 通过某种神秘的方法得到了小 B 的出牌顺序,现在他希望规划自己的出牌顺序使得自己在得分尽可能高的前提下出牌的字典序尽可能大。

\(1<=n<=10^5\)

题解:

考虑求最高的得分。

每个人肯定希望打比他低的又最高的。

所以排个序,扫一遍即可,正着扫倒着扫好像都可以。

现在要字典序最大,考虑逐位确定。

发现每一位可以二分,那么要支持删除的情况下动态维护最高得分。

显然可以线段树,要维护三个标记,已经匹配的,a剩下多少,b剩下多少。

直接搞就是\(O(n~log^2n)\)

考虑线段树多维护一个东西表示在这个区间删掉最左的之后的三个标记。

那就可以在线段树上面二分了,复杂度:\(O(n~log~n)\)

Code:

#include
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 1e5 + 5;

int n;
int a[N], b[N];

const int inf = 1e9;
struct nod {
    int x, y, z;
    nod() {
        x = y = z = 0;
    }
} t[N * 4], g[N * 4];

#define i0 i + i
#define i1 i + i + 1
void upd(nod &a, nod b, nod c) {
    if(b.z < 0 || c.z < 0) a.z = -inf; else
    a.z = b.z + c.z + min(c.x, b.y);
    a.y = c.y + max(0, b.y - c.x);
    a.x = b.x + max(0, c.x - b.y);
}

int pl, pr, px, py;

void add(int i, int x, int y) {
    if(x == y) {
        if(!py) t[i].x += px; else t[i].y += px;
        g[i] = t[i]; g[i].x --;
        if(g[i].x < 0) g[i].z = -inf;
        return;
    }
    int m = x + y >> 1;
    if(pl <= m) add(i0, x, m); else add(i1, m + 1, y);
    upd(t[i], t[i0], t[i1]);
    upd(g[i], g[i0], t[i1]);
    if(g[i].z < 0) upd(g[i], t[i0], g[i1]);
}

int ans, ans2[N];

nod pc, pd;
void qu(int i, int x, int y) {
    if(y < pl || x > pr) return;
    if(x >= pl && y <= pr) {
        upd(pc, pc, t[i]);
        return;
    }
    int m = x + y >> 1;
    qu(i0, x, m); qu(i1, m + 1, y);
}
struct P {
    int i, x, y;
} d[50]; int d0;
void ft(int i, int x, int y) {
    if(y < pl || x > pr) return;
    int m = x + y >> 1;
    if(x >= pl && y <= pr) {
        d[++ d0] = (P) {i, x, y};
        return;
    }
    ft(i0, x, m); ft(i1, m + 1, y);
}
nod p[50], q[50];
void dg(int i, int x, int y) {
    int m = x + y >> 1;
    nod pe; upd(pe, g[i], pd); upd(pe, pc, pe);
    if(pe.z < ans - py) return;
    if(x == y) {
        px = x; return;
    }
    pe = pc; upd(pc, pc, t[i0]);
    dg(i1, m + 1, y);
    pc = pe;
    if(px) return;
    pe = pd; upd(pd, t[i1], pd);
    dg(i0, x, m);
    pd = pe;
}
void gg() {
    d0 = 0; ft(1, 1, 1e5);
    p[0] = pc; q[d0 + 1] = pd;
    fo(i, 1, d0) upd(p[i], p[i - 1], t[d[i].i]);
    fd(i, d0, 1) upd(q[i], t[d[i].i], q[i + 1]);
    fd(i, d0, 1) {
        pc = p[i - 1], pd = q[i + 1];
        dg(d[i].i, d[i].x, d[i].y);
        if(px) return;
    }
}

int main() {
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    scanf("%d", &n);
    fo(i, 1, 1e5 * 4) g[i].z = -inf, g[i].x = -1;
    fo(i, 1, n) scanf("%d", &b[i]);
    fo(i, 1, n) scanf("%d", &a[i]);
    fo(i, 1, n) {
        pl = pr = b[i]; py = 1; px = 1;
        add(1, 1, 1e5);
        pl = pr = a[i]; py = 0; px = 1;
        add(1, 1, 1e5);
    }
    ans = t[1].z;
    fo(i, 1, n) {
        pl = pr = b[i]; px = -1; py = 1;
        add(1, 1, 1e5);
        pl = 1, pr = b[i]; pc = pd = nod();
        qu(1, 1, 1e5);
        py = 1, pl = b[i] + 1, pr = 1e5; px = 0;
        gg();
        if(px) {
            ans2[i] = px;
            ans --;
            pl = pr = px; py = 0; px = -1;
            add(1, 1, 1e5);
            continue;
        }
        pl = b[i] + 1, pr = 1e5; pc = pd = nod();
        qu(1, 1, 1e5); swap(pc, pd);
        py = 0, pl = 1, pr = b[i]; px = 0;
        gg();
        ans2[i] = px;
        pl = pr = px; py = 0; px = -1;
        add(1, 1, 1e5);
    }
    fo(i, 1, n) pp("%d ", ans2[i]); hh;
}

你可能感兴趣的:(【NOIP2019模拟11.01】Game(贪心+线段树))