[NOIP2017 TG D2T3]列队

题目大意:有一个$n \times m$的方阵,第$i$行第$j$列的人的编号是$(i-1) \times m + j$。

现在有$q$个出列操作,每次让一个人出列,然后让这个人所在行向左看齐,再让最后一列向前看齐,最后让这个人站到第$n$行第$m$列的位置。

你需要输出每次出列的人的编号。

题解:可以每行维护一棵平衡树,再给最后一列维护一棵平衡树(虽然正解是用树状数组)。

发现每行的人初始编号是连续的,而对于$9 \times 10 ^ {10}$的人数,$3 \times 10 ^ {5}$次询问改变的人数其实并不多。

所以,我们把同一行内编号连续的一段缩成一个点,然后需要出列时再分裂即可。

卡点:因为正解是树状数组,所以我洛谷被玄学卡$RE$($UOJ$过的),数组大小调了好几次,然后发现开$O(2)$,数组开的比$UOJ$大一些就不$RE$了。。。

C++ Code:(洛谷)

// luogu-judger-enable-o2
#include <cstdio>
#include <cstdlib>
#define maxn 5500010
using namespace std;
struct node{
    int l, r;
    long long head;
    void operator -= (long long a) {l -= a; r -= a;}
} val[maxn];
int n, m, q;
int lc[maxn], rc[maxn], num[maxn], sz[maxn], tg[maxn], idx;
struct treap {
    int root, len;
    int ta, tb, tmp, tmp6;
    int nw(node x) {
        val[++idx] = x;
        sz[idx] = 1;
        num[idx] = rand();
        lc[idx] = rc[idx] = 0;
        return idx;
    }
    void update(int p) {sz[p] = sz[lc[p]] + sz[rc[p]] + 1;}
    void pushdown(int p) {
        tmp6 = tg[p]; tg[p] = 0;
        val[lc[p]] -= tmp6;
        val[rc[p]] -= tmp6;
        tg[lc[p]] += tmp6;
        tg[rc[p]] += tmp6;
    }
    void splitl(int rt, int k, int &x, int &y) {
        if (!rt) x = y = 0;
        else {
            if (val[rt].l <= k) splitl(rc[rt], k, rc[rt], y), x = rt;
            else splitl(lc[rt], k, x, lc[rt]), y = rt;
            update(rt);
        }
    }
    void splitr(int rt, int k, int &x, int &y) {
        if (!rt) x = y = 0;
        else {
            pushdown(rt);
            if (val[rt].r <= k) splitr(rc[rt], k, rc[rt], y), x = rt;
            else splitr(lc[rt], k, x, lc[rt]), y = rt;
            update(rt);
        }
    }
    int merge(int x, int y) {
        if (!x || !y) return x | y;
        if (num[x] > num[y]) {
            pushdown(x);
            rc[x] = merge(rc[x], y);
            update(x);
            return x;
        } else {
            pushdown(y);
            lc[y] = merge(x, lc[y]);
            update(y);
            return (y);
        }
    }
    void build(node p) {
        root = nw(p);
    }
    void insert(long long p, int k = -1){
        if (k == -1) k = len;
        if (!root) root = nw((node) {k, k, p});
        else root = merge(root, nw((node) {k, k, p}));
    }
    long long nxt(int k) {
        splitr(root, k - 1, ta, tmp);
        splitl(tmp, k, tmp, tb);
        int a = nw((node) {val[tmp].l, k - 1, val[tmp].head}),
        b = nw((node) {k + 1, val[tmp].r, val[tmp].head + k + 1 - val[tmp].l});
        tb = merge(b, tb);
        tg[tb]++;
        val[tb] -= 1;
        root = merge(merge(ta, a), tb);
        return k - val[tmp].l + val[tmp].head;
    }
} s[300010], back;
int main() {
    srand(20040826);
    scanf("%d%d%d", &n, &m, &q);
    back.len = n;
    for (long long i = 1; i <= n; i++) back.insert(i * m, i);
    for (long long i = 1; i <= n; i++) {
        s[i].len = m - 1;
        s[i].build((node){1, m - 1, (i - 1) * m + 1});
    }
    int x, y;
    long long ta, tb;
    while (q--) {
        scanf("%d%d", &x, &y);
        if (y != m) {
            ta = s[x].nxt(y);
            tb = back.nxt(x);
            s[x].insert(tb);
            back.insert(ta);
        } else {
            ta = back.nxt(x);
            back.insert(ta);
        }
        printf("%lld\n", ta);
    }
    return 0;
} 

  

C++ Code:(UOJ)

#include <cstdio>
#include <cstdlib>
#define maxn 5000010
using namespace std;
struct node{
	int l, r;
	long long head;
	void operator -= (long long a) {l -= a; r -= a;}
} val[maxn];
int n, m, q;
int lc[maxn], rc[maxn], num[maxn], sz[maxn], tg[maxn], idx;
struct treap {
	int root, len;
	int ta, tb, tmp;
	int nw(node x) {
		val[++idx] = x;
		sz[idx] = 1;
		num[idx] = rand();
		lc[idx] = rc[idx] = 0;
		return idx;
	}
	void update(int p) {sz[p] = sz[lc[p]] + sz[rc[p]] + 1;}
	void pushdown(int p) {
		int tmp6 = tg[p]; tg[p] = 0;
		val[lc[p]] -= tmp6;
		val[rc[p]] -= tmp6;
		tg[lc[p]] += tmp6;
		tg[rc[p]] += tmp6;
	}
	void splitl(int rt, int k, int &x, int &y) {
		if (!rt) x = y = 0;
		else {
			if (val[rt].l <= k) splitl(rc[rt], k, rc[rt], y), x = rt;
			else splitl(lc[rt], k, x, lc[rt]), y = rt;
			update(rt);
		}
	}
	void splitr(int rt, int k, int &x, int &y) {
		if (!rt) x = y = 0;
		else {
			pushdown(rt);
			if (val[rt].r <= k) splitr(rc[rt], k, rc[rt], y), x = rt;
			else splitr(lc[rt], k, x, lc[rt]), y = rt;
			update(rt);
		}
	}
	int merge(int x, int y) {
		if (!x || !y) return x | y;
		if (num[x] > num[y]) {
			pushdown(x);
			rc[x] = merge(rc[x], y);
			update(x);
			return x;
		} else {
			pushdown(y);
			lc[y] = merge(x, lc[y]);
			update(y);
			return (y);
		}
	}
	void build(node p) {
		root = nw(p);
	}
	void insert(long long p, int k = -1){
		if (k == -1) k = len;
		if (!root) root = nw((node) {k, k, p});
		else root = merge(root, nw((node) {k, k, p}));
	}
	long long nxt(int k) {
		splitr(root, k - 1, ta, tmp);
		splitl(tmp, k, tmp, tb);
		int a = nw((node) {val[tmp].l, k - 1, val[tmp].head}),
		b = nw((node) {k + 1, val[tmp].r, val[tmp].head + k + 1 - val[tmp].l});
		tb = merge(b, tb);
		tg[tb]++;
		val[tb] -= 1;
		root = merge(merge(ta, a), tb);
		return k - val[tmp].l + val[tmp].head;
	}
} s[300010], back;
int main() {
	srand(20040826);
	scanf("%d%d%d", &n, &m, &q);
	back.len = n;
	for (long long i = 1; i <= n; i++) back.insert(i * m, i);
	for (long long i = 1; i <= n; i++) {
		s[i].len = m - 1;
		s[i].build((node){1, m - 1, (i - 1) * m + 1});
	}
	int x, y;
	long long ta, tb;
	while (q--) {
		scanf("%d%d", &x, &y);
		if (y != m) {
			ta = s[x].nxt(y);
			tb = back.nxt(x);
			s[x].insert(tb);
			back.insert(ta);
		} else {
			ta = back.nxt(x);
			back.insert(ta);
		}
		printf("%lld\n", ta);
	}
	return 0;
} 

  

猜你喜欢

转载自www.cnblogs.com/Memory-of-winter/p/9453046.html