【洛谷3822】[NOI2017] 整数(线段树压位)

题目:

洛谷 3822

分析:

直接按题意模拟,完了。

将每次加 / 减拆成不超过 \(32\) 个对单独一位的加 / 减。

考虑给一个二进制位(下称「当前位」)加 \(1\) 时,如果这一位本来就是 \(0\) ,那么直接变成 \(1\) 。否则要考虑进位:向左(以后默认从右向左为低位至高位,与书写顺序相同)找到第一个为 \(0\) 的位 \(p\) ,将其变成 \(1\) ,并把从 \(p\) 到当前位中间所有的 \(1\) 变成 \(0\)

减法是类似的。退位操作就是向左找到第一个 \(1\) ,将其变成 \(0\) ,并把中间所有 \(0\) 变成 \(1\)

以上找第一个 \(1\) 或者 \(0\) 和区间修改均可用线段树完成,只需要维护每个结点对应的区间是否全 \(0\) 或全 \(1\) 即可。

但是将一个询问拆成 \(32\) 次常数太大,\(3.2\times 10^7\) 次修改再带上线段树的 \(\log 3\times 10^7\) 根本过不去。考虑压位,线段树每个叶子表示连续多个(我的代码中使用的是 \(60\) 个)二进制位,找第一个 \(1\) / \(0\) 改为找第一个非 \(0\) (全 \(0\) ) / 非 \(2^{60}\) (全 \(1\) )的数。这样,每次修改只需要拆成最多对两个位置的加 / 减。复杂度 \(O(n\log m)\) 其中 \(m\) 是最大位数。

代码:

注意线段树上二分找第一个非全 \(0\) / 非全 \(1\) 的数的做法。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;

namespace zyt
{
	template<typename T>
	inline bool read(T &x)
	{
		char c;
		bool f = false;
		x = 0;
		do
			c = getchar();
		while (c != EOF && c != '-' && !isdigit(c));
		if (c == EOF)
			return false;
		if (c == '-')
			f = true, c = getchar();
		do
			x = x * 10 + c - '0', c = getchar();
		while (isdigit(c));
		if (f)
			x = -x;
		return true;
	}
	template<typename T>
	inline void write(T x)
	{
		static char buf[20];
		char *pos = buf;
		if (x < 0)
			putchar('-'), x = -x;
		do
			*pos++ = x % 10 + '0';
		while (x /= 10);
		while (pos > buf)
			putchar(*--pos);
	}
	typedef unsigned long long ull;
	const int N = 1e6 + 10, DIGIT = 60;
	const ull BASE = 1ULL << DIGIT;
	namespace Segment_Tree
	{
		struct node
		{
			ull val;
			bool all0, all1, tag0, tag1;
		}tree[N << 2];
		void cov0(const int rot)
		{
			tree[rot].val = 0;
			tree[rot].all0 = tree[rot].tag0 = true;
			tree[rot].all1 = tree[rot].tag1 = false;
		}
		void cov1(const int rot)
		{
			tree[rot].val = BASE - 1ULL;
			tree[rot].all1 = tree[rot].tag1 = true;
			tree[rot].all0 = tree[rot].tag0 = false;
		}
		void update(const int rot)
		{
			tree[rot].all0 = (tree[rot << 1].all0 && tree[rot << 1 | 1].all0);
			tree[rot].all1 = (tree[rot << 1].all1 && tree[rot << 1 | 1].all1);
		}
		void pushdown(const int rot)
		{
			if (tree[rot].tag0)
			{
				cov0(rot << 1), cov0(rot << 1 | 1);
				tree[rot].tag0 = false;
			}
			else if (tree[rot].tag1)
			{
				cov1(rot << 1), cov1(rot << 1 | 1);
				tree[rot].tag1 = false;
			}
		}
		void cover0(const int rot, const int lt, const int rt, const int ls, const int rs)
		{
			if (ls <= lt && rt <= rs)
			{
				cov0(rot);
				return;
			}
			int mid = (lt + rt) >> 1;
			pushdown(rot);
			if (ls <= mid)
				cover0(rot << 1, lt, mid, ls, rs);
			if (rs > mid)
				cover0(rot << 1 | 1, mid + 1, rt, ls, rs);
			update(rot);
		}
		void cover1(const int rot, const int lt, const int rt, const int ls, const int rs)
		{
			if (ls <= lt && rt <= rs)
			{
				cov1(rot);
				return;
			}
			int mid = (lt + rt) >> 1;
			pushdown(rot);
			if (ls <= mid)
				cover1(rot << 1, lt, mid, ls, rs);
			if (rs > mid)
				cover1(rot << 1 | 1, mid + 1, rt, ls, rs);
			update(rot);
		}
		void change(const int rot, const int lt, const int rt, const int pos, const ull x)
		{
			if (pos > rt)
				return;
			if (lt == rt)
			{
				tree[rot].val = x;
				tree[rot].all0 = (x == 0);
				tree[rot].all1 = (x == (BASE - 1ULL));
				return;
			}
			int mid = (lt + rt) >> 1;
			pushdown(rot);
			if (pos <= mid)
				change(rot << 1, lt, mid, pos, x);
			else
				change(rot << 1 | 1, mid + 1, rt, pos, x);
			update(rot);
		}
		ull query(const int rot, const int lt, const int rt, const int pos)
		{
			if (lt == rt)
				return tree[rot].val;
			int mid = (lt + rt) >> 1;
			pushdown(rot);
			if (pos <= mid)
				return query(rot << 1, lt, mid, pos);
			else
				return query(rot << 1 | 1, mid + 1, rt, pos);
		}
		int find0(const int rot, const int lt, const int rt, const int pos)
		{
			if (lt == rt)
				return lt;
			int mid = (lt + rt) >> 1;
			pushdown(rot);
			if (pos <= mid && !tree[rot << 1].all1)
			{
				int ans = find0(rot << 1, lt, mid, pos);
				if (ans <= N)
					return ans;
			}
			if (!tree[rot << 1 | 1].all1)
				return find0(rot << 1 | 1, mid + 1, rt, pos);
			else
				return N + 1;
		}
		int find1(const int rot, const int lt, const int rt, const int pos)
		{
			if (lt == rt)
				return lt;
			int mid = (lt + rt) >> 1;
			pushdown(rot);
			if (pos <= mid && !tree[rot << 1].all0)
			{
				int ans = find1(rot << 1, lt, mid, pos);
				if (ans <= N)
					return ans;
			}
			if (!tree[rot << 1 | 1].all0)
				return find1(rot << 1 | 1, mid + 1, rt, pos);
			else
				return N + 1;
		}
		void init()
		{
			cov0(1);
		}
	}
	ull extract(const ull a, const int l, const int r)
	{
		return (a & ((1ULL << r) - 1ULL)) >> l;
	}
	bool check(const ull a, const int p)
	{
		return a & (1ULL << p);
	}
	void add(const ull a, const int p)
	{
		using namespace Segment_Tree;
		ull now = query(1, 0, N, p);
		if (now + a >= BASE)
		{
			int pos = find0(1, 0, N, p + 1);
			ull tmp = query(1, 0, N, pos);
			change(1, 0, N, pos, tmp + 1ULL);
			if (pos > p + 1)
				cover0(1, 0, N, p + 1, pos - 1);
		}
		change(1, 0, N, p, (now + a) % BASE);
	}
	void sub(const ull a, const int p)
	{
		using namespace Segment_Tree;
		ull now = query(1, 0, N, p);
		if (now < a)
		{
			int pos = find1(1, 0, N, p + 1);
			ull tmp = query(1, 0, N, pos);
			change(1, 0, N, pos, tmp - 1ULL);
			if (pos > p + 1)
				cover1(1, 0, N, p + 1, pos - 1);
		}
		change(1, 0, N, p, (now - a + BASE) % BASE);
	}
	int work()
	{
		using namespace Segment_Tree;
		int n, t1, t2, t3;
		read(n), read(t1), read(t2), read(t3);
		init();
		while (n--)
		{
			int opt;
			read(opt);
			if (opt == 1)
			{
				int a, b;
				read(a), read(b);
				if (a > 0) //ADD
				{
					add(extract(a, 0, DIGIT - b % DIGIT) << (b % DIGIT), b / DIGIT);
					add(extract(a, DIGIT - b % DIGIT, DIGIT), b / DIGIT + 1);
				}
				else if (a < 0)
				{
					a = -a;
					sub(extract(a, 0, DIGIT - b % DIGIT) << (b % DIGIT), b / DIGIT);
					sub(extract(a, DIGIT - b % DIGIT, DIGIT), b / DIGIT + 1);
				}
			}
			else
			{
				int k;
				read(k);
				write(check(query(1, 0, N, k / DIGIT), k % DIGIT) ? 1 : 0), putchar('\n');
			}
		}
		return 0;
	}
}
int main()
{
#ifdef BlueSpirit
	freopen("3822.in", "r", stdin);
	freopen("3822.out", "w", stdout);
#endif
	return zyt::work();
}
posted @ 2019-06-09 13:49  Inspector_Javert  阅读(128)  评论(0编辑  收藏  举报