【清华集训2017模拟】Sequence

Description :

【清华集训2017模拟】Sequence_第1张图片
1<=n<=3e4,1<=m<=1e5

题解:

做的第一道整体二分题。

整体二分的套路:
二分一个答案,对于每个询问求出答案,再把询问分成两部分,第一个部分是答案< mid的,第二个部分是答案大于mid的,至于=mid的,这个询问不一定是mid,先把答案记一下,根据实际情况而决定分到哪个部分。

这题首先得把[x, y]的限制具体化,用一棵主席树预处理[x..y]限制对应的a值是什么。

二分一个答案mid,把b值小于等于mid的元素加入坐标系,横坐标是a值,纵坐标是序号。
对于每个询问,相当于询问[l..r][x_..y_]的点数(x_、y_是x,y具体化以后的a值大小),这个可以拆开,排个序,用个扫描线加树状数组的东西维护一下就可以了。

求出答案,需要把询问分到它们应该去的部分,s>=mid,则说明答案<=mid,s

#include
#include
#include
#define low(x) ((x) & -(x))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 100005;

int n, m;

struct node {
    int fi, se, num;
}a[N];

struct tree {
    int l, r, s;
}t[N * 100];
int g[N], tp;
int opl, opr, opx;

struct Ask {
    int l, r, x, y, k, to, ans, s;
}b[N];

int xg(int i, int l, int r) {
    if(l > opl || r < opl) return i;
    if(l == r) {
        t[++ tp] = t[i]; t[tp].s ++;
        return tp;
    }
    int m = l + r >> 1; t[++ tp] = t[i]; i = tp;
    t[i].l = xg(t[i].l, l, m); t[i].r = xg(t[i].r, m + 1, r);
    t[i].s = t[t[i].l].s + t[t[i].r].s;
    return i;
}
int query(int g1, int g2, int l, int r, int v) {
    if(l == r) return l;
    int m = l + r >> 1, l1 = t[g1].l, l2 = t[g2].l;
    if(t[l2].s - t[l1].s >= v)
        return query(t[g1].l, t[g2].l, l, m, v); else
        return query(t[g1].r, t[g2].r, m + 1, r, v - (t[l2].s - t[l1].s));
}

void Init() {
    scanf("%d", &n);
    fo(i, 1, n) scanf("%d", &a[i].fi);
    fo(i, 1, n) scanf("%d", &a[i].se);
    g[0] = tp = 1;
    fo(i, 1, n) {
        opl = a[i].fi;
        g[i] = xg(g[i - 1], 1, n);
    }
    scanf("%d", &m);
    fo(i, 1, m) {
        scanf("%d %d %d %d %d", &b[i].l, &b[i].r, &b[i].x, &b[i].y, &b[i].k);
        b[i].x = query(g[b[i].l - 1], g[b[i].r], 1, n, b[i].x);
        b[i].y = query(g[b[i].l - 1], g[b[i].r], 1, n, b[i].y);
    }
}

struct node2 {
    int h, x, y, f, to;
}c[N * 2];

bool rank_c(node2 a, node2 b) {
    return a.h < b.h;
}

struct node3 {
    int h, w;
}d[N];

bool rank_d(node3 a, node3 b) {
    return a.h < b.h;
}

int f[N];

void add(int x, int y) {
    while(x <= n) {
        f[x] += y;
        x += low(x);
    }
}

int find(int x) {
    int s = 0;
    while(x) {
        s += f[x];
        x -= low(x);
    }
    return s;
}

bool rank_b(Ask a, Ask b) {
    if(a.s < a.k && b.s >= b.k) return 0;
    if(a.s > a.k && b.s <= b.k) return 1;
    if(a.s == a.k) {
        if(b.s < b.k) return 1;
        if(b.s > b.k) return 0;
    }
    return 0;
}

void EF(int x, int y, int ax, int ay, int bx, int by) {
    if(x > y) return;
    if(ax > ay) return;
    if(bx > by) return;
    int mid = (x + y) / 2;

    int tc = 0;
    fo(i, bx, by) {
        b[i].s = 0;

        c[++ tc].f = -1;
        c[tc]. h = b[i].l - 1;
        c[tc].x = b[i].x; c[tc].y = b[i].y;
        c[tc].to = i;

        c[++ tc].f = 1;
        c[tc]. h = b[i].r;
        c[tc].x = b[i].x; c[tc].y = b[i].y;
        c[tc].to = i;
    }
    sort(c + 1, c + tc + 1, rank_c);
    int neway = ay;
    while(neway >= ax && a[neway].se > mid) neway --; //find neway
    int td = 0;
    fo(i, ax, neway) d[++ td].h = a[i].num, d[td].w = a[i].fi;
    sort(d + 1, d + td + 1, rank_d);

    int dz = 0;
    fo(i, 1, tc) {
        while(dz < td && d[dz + 1].h <= c[i].h) dz ++, add(d[dz].w, 1);
        b[c[i].to].s += c[i].f * (find(c[i].y) - find(c[i].x - 1));
    }
    while(dz) add(d[dz].w, -1), dz --; //clear f
    sort(b + bx, b + by + 1, rank_b);
    fo(i, bx, by) if(b[i].s == b[i].k) b[i].ans = mid;

    int newby = by;
    while(newby >= bx && b[newby].s < b[newby].k) newby --;

    int newbx = bx;
    while(newbx <= by && b[newbx].s >= b[newbx].k) newbx ++;

    fo(i, newbx, by) b[i].k -= b[i].s;
    EF(x, mid - 1, ax, neway, bx, newby);
    EF(mid + 1, y, neway + 1, ay, newbx, by);

}

bool rank_a(node a, node b) {
    return a.se < b.se;
}

int ans[N];

int main() {
    freopen("sequence.in", "r", stdin); 
    freopen("sequence.out", "w", stdout);
    Init();
    fo(i, 1, n) a[i].num = i;
    sort(a + 1, a + n + 1, rank_a);
    fo(i, 1, m) b[i].to = i;
    EF(1, n, 1, n, 1, m);
    fo(i, 1, m) ans[b[i].to] = b[i].ans;
    fo(i, 1, m) printf("%d\n", ans[i]);
}

你可能感兴趣的:(扫描线,分治,整体二分)