【bzoj4942】[Noi2017]整數 壓位+線段樹


題目描述

P 博士將他的計算任務抽象為對一個整數的操作。

具體來說,有一個整數 $x$ ,一開始為0。

接下來有 $n$ 個操作,每個操作都是以下兩種類型中的一種:

1 a b :將 $x$ 加上整數 $a⋅2^b$ ,其中 $a$ 為一個整數,$b$ 為一個非負整數
2 k :詢問 $x$ 在用二進制表示時,位權為 $2^k$ 的位的值(即這一位上的 $1$ 代表 $2^k$ )

保證在任何時候,$x≥0$。

輸入

從標准輸入讀入數據。

輸入的第一行包含四個正整數 $n,t_1,t_2,t_3$ ,$n$ 的含義見題目描述,$t_1,t_2,t_3$ 的具體含義見子任務。

接下來 $n$ 行,每行給出一個操作,具體格式和含義見題目描述。

同一行輸入的相鄰兩個元素之間,用恰好一個空格隔開。

輸出

輸出到標准輸出。

對於每個詢問操作,輸出一行,表示該詢問的答案(0或1)。 對於加法操作,沒有任何輸出。

樣例輸入

10 3 1 2
1 100 0
1 2333 0
1 -233 0
2 5
2 7
2 15
1 5 15
2 15
1 -1 12
2 15

樣例輸出

0
1
0
1
0


題解

壓位+線段樹

考場上想出了 $n\log^2n$ 的做法,然而沒有想出壓位就GG了。。。

先說 $n\log^2n$ 的做法:

由於 $|a|$ 只有 $10^9$ ,因此把 $a$ 分解成 $\sum 2^i$ 的形式,就變成了 $\log n$ 次加/減 $2^k$ 。

考慮加法,如果這一位是0則直接改為1,否則進位,相當於這一位變成0,下一位+1。最終結果就是找到這一位下面第一個為0的位,把該位+1,[當前位,該位)變成0。

減法同理,如果是1則直接該為0,否則退位,找到這一位下面第一個為1的為,把該位-1,[當前位,該位)變成1。

使用線段樹維護區間是否全為0/1,單次加減的時間復雜度就是 $\log n$ ,$n$ 次 $\log n$ 次加減,時間復雜度 $O(n\log^2n)$ ,這樣只有68分。

考慮優化:維護01信息過於浪費,考慮壓位,維護每連續30位的數是什么。這樣一次操作最多給兩個位加減,加法就判斷這一位加了以后是否超過 $2^{30}-1$ ,超過就找下面第一個不為 $2^{30}-1$ 的位,把該位+1,(當前位,該位)變成0,當前位直接變成進位后的結果。減法同理。

其中維護一段區間是否全 $0$ /全 $2^{30}-1$ 可以通過維護區間或和區間與來解決。

這樣壓位后時間復雜度就變為了 $O(n\log n)$ 

#include <cstdio>
#include <cctype>
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
const int m = 1000010 , s = (1 << 30) - 1;
int vo[m << 2] , va[m << 2] , tag[m << 2];
char pbuf[2000010] , *pp = pbuf;
inline char nc()
{
	static char buf[100000] , *p1 , *p2;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
}
inline int read()
{
	int ret = 0 , f = 0; char ch = nc();
	while(!isdigit(ch)) f |= (ch == '-') , ch = nc();
	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
	return f ? -ret : ret;
}
inline void pushup(int x)
{
	vo[x] = vo[x << 1] | vo[x << 1 | 1];
	va[x] = va[x << 1] & va[x << 1 | 1];
}
inline void pushdown(int x)
{
	if(~tag[x])
	{
		int l = x << 1 , r = x << 1 | 1;
		vo[l] = vo[r] = va[l] = va[r] = tag[l] = tag[r] = tag[x];
		tag[x] = -1;
	}
}
inline void fix(int p , int v , int l , int r , int x)
{
	if(l == r)
	{
		vo[x] += v , va[x] += v;
		return;
	}
	pushdown(x);
	int mid = (l + r) >> 1;
	if(p <= mid) fix(p , v , lson);
	else fix(p , v , rson);
	pushup(x);
}
inline void update(int b , int e , int v , int l , int r , int x)
{
	if(b <= l && r <= e)
	{
		vo[x] = va[x] = tag[x] = v;
		return;
	}
	pushdown(x);
	int mid = (l + r) >> 1;
	if(b <= mid) update(b , e , v , lson);
	if(e > mid) update(b , e , v , rson);
	pushup(x);
}
inline int findzero(int p , int l , int r , int x)
{
	if(!vo[x]) return -1;
	if(l == r) return l;
	pushdown(x);
	int mid = (l + r) >> 1;
	if(p <= mid)
	{
		int t = findzero(p , lson);
		return ~t ? t : findzero(p , rson);
	}
	else return findzero(p , rson);
}
inline int findinf(int p , int l , int r , int x)
{
	if(va[x] == s) return -1;
	if(l == r) return l;
	pushdown(x);
	int mid = (l + r) >> 1;
	if(p <= mid)
	{
		int t = findinf(p , lson);
		return ~t ? t : findinf(p , rson);
	}
	else return findinf(p , rson);
}
inline int query(int p , int l , int r , int x)
{
	if(l == r) return va[x];
	pushdown(x);
	int mid = (l + r) >> 1;
	if(p <= mid) return query(p , lson);
	else return query(p , rson);
}
inline void add(int p , int a)
{
	int t = query(p , 0 , m , 1);
	if(t + a <= s) fix(p , a , 0 , m , 1);
	else
	{
		fix(p , a - s - 1 , 0 , m , 1);
		int q = findinf(p + 1 , 0 , m , 1);
		if(q != p + 1) update(p + 1 , q - 1 , 0 , 0 , m , 1);
		fix(q , 1 , 0 , m , 1);
	}
}
inline void del(int p , int a)
{
	int t = query(p , 0 , m , 1);
	if(t - a >= 0) fix(p , -a , 0 , m , 1);
	else
	{
		fix(p , s + 1 - a , 0 , m , 1);
		int q = findzero(p + 1 , 0 , m , 1);
		if(q != p + 1) update(p + 1 , q - 1 , s , 0 , m , 1);
		fix(q , -1 , 0 , m , 1);
	}
}
int main()
{
	int n = read() , opt , x , y , p;
	read() , read() , read();
	while(n -- )
	{
		opt = read() , x = read();
		if(opt == 1)
		{
			y = read() , p = y / 30;
			if(x > 0)
			{
				add(p , (x << (y - p * 30)) & s);
				add(p + 1 , x >> (30 - (y - p * 30)));
			}
			else if(x < 0)
			{
				x = -x;
				del(p , (x << (y - p * 30)) & s);
				del(p + 1 , x >> (30 - (y - p * 30)));
			}
		}
		else
		{
			p = x / 30;
			*pp ++ = (query(p , 0 , m , 1) & (1 << (x - p * 30)) ? 1 : 0) ^ '0' , *pp ++ = '\n';
		}
	}
	fwrite(pbuf , 1 , pp - pbuf , stdout);
	return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM