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 一块给我们讲了很久,但是仍旧不明白如何实现。当一年之后,我已经熟练掌握了状压和线段树,再看到这个题,心里只想着一句话「这题似乎不是特别难」,即使这样,这题在我心里也有着不同于其它题目的分量。

感谢扶苏学长。撒花~

posted @ 2021-09-28 21:45  ·Iris  阅读(118)  评论(0编辑  收藏  举报