【NOI2018全国热身赛】小X的城池

【题目链接】

【思路要点】

  • 我们采用线段树直接维护整个序列,每个节点代表一个区间,并在节点上记录:
  • 1、在区间内已经找到了人口更高的\(B\)的\(A\)的数量\(Ans\)
  • 2、区间内的道路是否全部向左\(All\)或向右\(Arr\)。
  • 3、区间内只能到达左端点/只能到达右端点/既可以到达左端点又可以到达右端点的,人口为\(k(0≤k≤75)\)的,还未找到人口更高的\(B\)的\(A\)的数量\(Al_k,Ar_k,Am_k\)(实际上\(Am_k\)至多有一位非零0,但笔者当场没有想到)。
  • 4、从区间的左/右端点是否能够到达人口超过\(k(0≤k≤75)\)的\(B\)型城市\(Bl_k,Br_k\)。
  • 并且记录区间中所有边反向后的上述信息。
  • 上述信息是可以合并的,在实现了信息的合并后,剩余的工作就是线段树的基本操作了。
  • 注意本题卡空间,因此笔者只在线段树的非叶节点上存储了上述信息。
  • 时间复杂度\(O((N+QLogN)*(LogN+V))\),其中\(V=75\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100002;
const int MAXM = 77;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct BinaryIndexTree {
	int n; bool a[MAXN];
	void init(int x) {
		n = x - 1;
		for (int i = 1; i <= n; i++)
			a[i] = false;
	}
	void modify(int l, int r) {
		for (int i = l; i <= n; i += i & -i)
			a[i] ^= true;
		for (int i = r; i <= n; i += i & -i)
			a[i] ^= true;
	}
	bool query(int pos) {
		bool ans = false;
		for (int i = pos; i >= 1; i -= i & -i)
			ans ^= a[i];
		return ans;
	}
} edge;
int n, m, val[MAXN];
char type[MAXN];
struct info {
	int ans, al[MAXM], am[MAXM], ar[MAXM];
	bool bl[MAXM], br[MAXM], all, arr;
};
info operator + (const info &a, const info &b) {
	info ans;
	memcpy(ans.al, a.al, sizeof(a.al));
	memcpy(ans.ar, b.ar, sizeof(b.ar));
	memset(ans.am, 0, sizeof(ans.am));
	memset(ans.bl, false, sizeof(ans.bl));
	memset(ans.br, false, sizeof(ans.br));
	ans.ans = a.ans + b.ans;
	ans.all = false;
	ans.arr = a.arr && b.arr;
	for (int i = 0; i <= 75; i++) {
		if (b.bl[i + 1]) ans.ans += a.ar[i] + a.am[i];
		else if (b.arr) ans.ar[i] += a.ar[i], ans.am[i] += a.am[i];
		else ans.al[i] += a.am[i];
		ans.ar[i] += b.am[i];
		ans.br[i] = b.br[i];
		if (a.arr) ans.bl[i] = a.bl[i] | b.bl[i];
		else ans.bl[i] = a.bl[i];
	}
	return ans;
}
info operator - (const info &a, const info &b) {
	info ans;
	memcpy(ans.al, a.al, sizeof(a.al));
	memcpy(ans.ar, b.ar, sizeof(b.ar));
	memset(ans.am, 0, sizeof(ans.am));
	memset(ans.bl, false, sizeof(ans.bl));
	memset(ans.br, false, sizeof(ans.br));
	ans.ans = a.ans + b.ans;
	ans.all = a.all && b.all;
	ans.arr = false;
	for (int i = 0; i <= 75; i++) {
		if (a.br[i + 1]) ans.ans += b.al[i] + b.am[i];
		else if (a.all) ans.al[i] += b.al[i], ans.am[i] += b.am[i];
		else ans.ar[i] += b.am[i];
		ans.al[i] += a.am[i];
		ans.bl[i] = a.bl[i];
		if (b.all) ans.br[i] = a.br[i] | b.br[i];
		else ans.br[i] = b.br[i];
	}
	return ans;
}
info unita(int val) {
	info ans;
	memset(ans.al, 0, sizeof(ans.al));
	memset(ans.ar, 0, sizeof(ans.ar));
	memset(ans.bl, 0, sizeof(ans.bl));
	memset(ans.br, 0, sizeof(ans.br));
	memset(ans.am, 0, sizeof(ans.am));
	ans.am[val]++;
	ans.ans = 0;
	ans.all = ans.arr = true;
	return ans;
}
info unitb(int val) {
	info ans;
	memset(ans.al, 0, sizeof(ans.al));
	memset(ans.ar, 0, sizeof(ans.ar));
	memset(ans.bl, 0, sizeof(ans.bl));
	memset(ans.br, 0, sizeof(ans.br));
	memset(ans.am, 0, sizeof(ans.am));
	for (int i = 0; i <= val; i++)
		ans.bl[i] = ans.br[i] = true;
	ans.ans = 0;
	ans.all = ans.arr = true;
	return ans;
}
info unit(int pos) {
	if (type[pos] == 'A') return unita(val[pos]);
	else return unitb(val[pos]);
}
struct SegmentTree {
	struct Node {
		int lc, rc, home;
		bool tag;
	} a[MAXN * 2];
	info inf[MAXN], rev[MAXN];
	int tot, root, size, n;
	void update(int root, int mid) {
		int pos = a[root].home, lc = a[root].lc, rc = a[root].rc;
		bool tmp = edge.query(mid);
		if (tmp) {
			inf[pos] = (a[lc].home ? inf[a[lc].home] : unit(mid)) - (a[rc].home ? inf[a[rc].home] : unit(mid + 1));
			rev[pos] = (a[lc].home ? rev[a[lc].home] : unit(mid)) + (a[rc].home ? rev[a[rc].home] : unit(mid + 1));
		} else {
			inf[pos] = (a[lc].home ? inf[a[lc].home] : unit(mid)) + (a[rc].home ? inf[a[rc].home] : unit(mid + 1));
			rev[pos] = (a[lc].home ? rev[a[lc].home] : unit(mid)) - (a[rc].home ? rev[a[rc].home] : unit(mid + 1));
		}
	}
	void pushdown(int root) {
		if (a[root].tag) {
			int tmp = a[root].lc;
			if (a[tmp].home) swap(inf[a[tmp].home], rev[a[tmp].home]);
			a[tmp].tag ^= true;
			tmp = a[root].rc;
			if (a[tmp].home) swap(inf[a[tmp].home], rev[a[tmp].home]);
			a[tmp].tag ^= true;
			a[root].tag = false;
		}
	}
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) return;
		a[root].home = ++tot;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
		update(root, mid);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void modify(int root, int l, int r, int pos, int d) {
		if (l == r) {
			val[l] = d;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= pos) modify(a[root].lc, l, mid, pos, d);
		else modify(a[root].rc, mid + 1, r, pos, d);
		update(root, mid);
	}
	void modify(int pos, int val) {
		modify(root, 1, n, pos, val);
	}
	void reverse(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) {
			a[root].tag ^= true;
			if (a[root].home) swap(inf[a[root].home], rev[a[root].home]);
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) reverse(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) reverse(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		update(root, mid);
	}
	void reverse(int l, int r) {
		edge.modify(l, r);
		reverse(root, 1, n, l, r);
	}
} ST;
int main() {
	read(n), read(m);
	edge.init(n);
	for (int i = 1; i <= n; i++) {
		read(val[i]);
		type[i] = getchar();
		while (type[i] != 'A' && type[i] != 'B') type[i] = getchar();
	}
	ST.init(n);
	while (m--) {
		static char s[15]; int x, y;
		scanf("\n%s%d%d", s, &x, &y);
		if (s[0] == 'U') ST.modify(x, y);
		else ST.reverse(x, y);
		if (n == 1) writeln(0);
		else writeln(ST.inf[ST.a[ST.root].home].ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值