1. 程式人生 > 其它 >P5522 [yLOI2019] 棠梨煎雪(線段樹、狀壓)

P5522 [yLOI2019] 棠梨煎雪(線段樹、狀壓)

首先看到題目要求實現區間詢問和單點修改,又發現題目有一條特殊性質——\(n\leq30\)。由此可以想到對 \(m\) 建一棵線段樹,每個節點維護字串壓縮後的資訊。

接下來思考如何狀壓這個字串。字串中有三種字元 0,1,?。可以將 0 狀壓為 \((10)_2\)1 狀壓為 \((01)_2\)? 狀壓為 \((11)_2\)。每次問一個區間內字串的方案數就相當於將所有點進行 & 運算,這樣可以保證 ? 在碰到 01 的時候可以直接變成對應數字,而當一個 01& 運算的時候對應值會變為 0 ,在查詢的時候方便特判無解的情況。

以上操作對修改同理。


一些小細節:線段樹走到空區間的時候傳一個 unsigned long long

型的二進位制位均為 1 的整數即可。

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define Int unsigned long long

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

#define lowbit(x) (x&(-x))
#define gb(x) ((x-1)/T + 1)
#define gl(x) ((x-1)*T + 1)
#define pb push_back
#define fi first
#define sd second
#define Re register
#define MOD(x) (x = (x + mod)%mod)
#define pii pair<int,int>

const int np = 1e5 + 15;

char c[np][40];
unsigned int INF;
int a[np];
char s[np];

struct node{
	int l,r;
	int state;
	node *ls,*rs;
	
	inline bool inrange(int L,int R){return L <= l && r <= R;}
	inline bool outofrange(int L,int R){return r<L||R<l;} 

	inline void pushup()
	{
		state = ls->state & rs->state ; 
	}
	
	inline void upd(int L,int R,int vl)
	{
		if(inrange(L,R))
		{
			state = vl;
			return;
		}
		else
		{
			if(!outofrange(L,R))
			{
				ls->upd(L,R,vl);
				rs->upd(L,R,vl);
				pushup();
			}
			else return;
		}
	}
	
	inline int query(int L,int R)
	{
		if(inrange(L,R))
		{
			return state;
		}
		else
		{
			if(!outofrange(L,R))
			{
				return ls->query(L,R) & rs->query(L,R);
			}
			else return INF;
		}
	}
}mem[np * 2],*pool = mem,*rot;
inline node *New(){return ++pool;}
inline node *build(int L,int R)
{
	node *u = New();
	u->l = L, u->r = R;
	if(L == R)
	{
		u->ls = u->rs = NULL;
		u->state = a[L];
	}
	else
	{
		int mid = L + R >> 1;
		u->ls = build(L,mid);
		u->rs = build(mid + 1,R);
		u->pushup();
	}
	return u;
}

inline int solve(char *g,int len)
{
	int state = 0;
	for(int i=1;i<=len;i++)
	{
		if(g[i] == '1')
		{
			state += 1ll<<(2*(i-1));
		}
		if(g[i] == '0')
		{
			state += 2ll<<(2*(i-1));
		}
		if(g[i] == '?')
		{
			state += 3ll<<(2*(i-1));
		}
	}
	return state;	
}

signed main()
{
	INF = 0;
	INF--;
	int n,m,q;
	read(n);
	read(m);
	read(q);
	for(int i=1;i<=m;i++)
	{
		scanf("%s",c[i] + 1);
		int op = strlen(c[i] + 1);
		a[i] = solve(c[i],op);
	}
	
	rot = build(1,m);
	
	int Ans = 0;
	for(int i=1,opt,l,r,pos;i<=q;i++)
	{
		read(opt);
		switch(opt)
		{
			case 0:{
				read(l);
				read(r);
				int k = rot->query(l,r);
				int g = 3,ans = 0;
				for(int i=0;i<2 * n;i+=2)
				{
					if((g<<i&k) == g<<i) ans++;
					if(!((k>>i)&3))
					{
						ans = -1;
						break;
					}
				 } 
				if(ans == -1) Ans ^= 0;
				else Ans ^= (1ll<<ans);

				break;
			}
			case 1:{
				read(pos);
				scanf("%s",s+1);
				int p_ = solve(s,strlen(s + 1));
				rot->upd(pos,pos,p_);
				break;
			}
		}
	}
	printf("%lld\n",Ans);
 }

End

2020 年暑假 @一扶蘇一 給我們講了線段樹,這種指標形式實現的線段樹就是當年的成果。這道題就是當時的作業題,當時機房裡只有 cyc 做了出來。當時扶蘇和 cyc 一塊給我們講了很久,但是仍舊不明白如何實現。當一年之後,我已經熟練掌握了狀壓和線段樹,再看到這個題,心裡只想著一句話「這題似乎不是特別難」,即使這樣,這題在我心裡也有著不同於其它題目的分量。

感謝扶蘇學長。撒花~