【洛谷3822】[NOI2017] 整数(线段树压位)
题目:
分析:
直接按题意模拟,完了。
将每次加 / 减拆成不超过 \(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();
}

                
            
        
浙公网安备 33010602011771号