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


    题目:

    洛谷 3822

    分析:

    直接按题意模拟,完了。

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

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

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

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

    但是将一个询问拆成 (32) 次常数太大,(3.2 imes 10^7) 次修改再带上线段树的 (log 3 imes 10^7) 根本过不去。考虑压位,线段树每个叶子表示连续多个(我的代码中使用的是 (60) 个)二进制位,找第一个 (1) / (0) 改为找第一个非 (0) (全 (0) ) / 非 (2^{60}) (全 (1) )的数。这样,每次修改只需要拆成最多对两个位置的加 / 减。复杂度 (O(nlog 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('
    ');
    			}
    		}
    		return 0;
    	}
    }
    int main()
    {
    #ifdef BlueSpirit
    	freopen("3822.in", "r", stdin);
    	freopen("3822.out", "w", stdout);
    #endif
    	return zyt::work();
    }
    
  • 相关阅读:
    [转]ASP.NET会话(Session)保存模式
    ASP.NET 2.0 实现伪静态网页方法
    显示带颜色的字符串
    sublime text 3.0使用
    sublime text插件
    cogs1715 动态逆序对
    双网卡bond
    解决CentOS6不能使用yum源
    查看磁盘io占用
    [office] 在word中的小技巧
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/10993308.html
Copyright © 2020-2023  润新知