P5522 [yLOI2019] 棠梨煎雪

发布时间 2023-08-13 20:27:58作者: Ian8877

题目链接:P5522 [yLOI2019] 棠梨煎雪

听着<<棠梨煎雪>>写棠梨煎雪,太美妙了!

题意:

对于一些给定的字符串,每个字符串只包含\(0,1\)以及?号。对于一个字符串\(s_1\),如果另一个不包含?号的串\(s_2\)对于每一个位置\(i\)满足:当\(s_{1,i}\)不为?,\(s_{2,i}=s_{1,i}\),反之任意即可;就说\(s_2\)匹配\(s_1\)

题目要求我们在线求出对于一段区间\([l,r]\)之间所有字符串都匹配的字符串共有多少,同时支持将位置\(x\)的字符串修改的操作。

分析:

可以发现,\(n\)十分的微小,仅仅只有\(30\),所以我们可以发现一种显然的方法:暴力地开30棵线段树,然后把每个字符串每个字符的状态全部记录下来,简单易懂的操作。

不过同样显然地,时间复杂度\(O(q~log_mn)\),题面给出的\(q\)最大为\(10^6\)\(m\)最大为\(10^5\),这是我们就会发现它暴了。所以,我们需要寻求更高效地方法,比如把\(n\)消去就不失为一种不错的选择,接下来我们就探讨如何将\(n\)消去。

注意到,一个字符的状态数很少,只存在3种,所以我们可以往位运算上思考。单纯一个整数不够表示,我们就是用两个整数来表示一个字符串的状态。

对于一个字符串\(s_x\),我们设整数\(p_x\)的二进制形式表示一个字符串某位是否确定,即是否为'?,比方说一个串"1100?",的\(p\)就为\((11110) _2\); 又设\(q_x\)的二进制形式表示一个字符串某位具体是什么,其中'0'为0,'1'为1,'?'为0,比方还是"1100?",它的\(q\)就为\((11000)_2\)。这样就可以简单地把一个串的主要信息表示出来了。

然后就是问题的重点:如何合并两个字符串。现在假设有两个字符串\(s_1,s_2\),那我们相应的就有\(p_1,q_1,p_2,q_2\),意义同上。

接下来,我们可以设\(p=p1~xor~p2\),发现此时在\(p\)之中,如果对于某一位\(i\)\(p_{1,i}=p_{2,i}\),那么\(p_i\)就为0,反之为1(异或的定义),所以\(p\)的意义即是合法情况下对于两个串\(s_1,s_2\)允许出现不同的位置,因为如果两个串的某个位置都确定,两个串的该位置必须相等,不然还可以不同。

再往后,我们仿照上面形式设\(q=q_1~xor~q_2\),然后\(q\)表示的就是两个串实际不同的的位置。比如对于串"1100?"与"10001"的\(p\)\((00001)_2\)\(q\)就为\((01001)_2\)。这时我们根据定义就会发现,如果在\(q\)内为1的位置,在\(p\)内不为1,说明这两个串是冲突的,写成位运算判断就是\((p|q)!=p\),两串冲突。

万一两串经过上述判断不冲突,我们就可以使合并后的\(p_x=p_1|p_2,q_x=q_1|q_2\),毕竟根据定义,两串只要其中一个串的某位置确定了,合并后该位置必定确定,而该位置的确定值也是由相似的方式决定的。

上述说完,我们成功将\(O(n)\)的合并操作优化为了\(O(1)\),此时只用将其套入线段树内便可以以\(O(q~logm)\)的时间复杂度解决本题。

实现:

本题好像也没什么注意事项,思路对了还是很好搞得,那就直接放代码把:

#include<algorithm>
#include<stdio.h>
#include<queue>
#define maxn 100010
using namespace std;
int n,m,t;
char s[maxn][35];
struct tree {
	int l,r;
	int p,q,tag;
} T[maxn<<2];
int ls(int x) {return x<<1;}
int rs(int x) {return x<<1|1;}
void push_up(tree &x,tree l,tree r) {
	if(l.tag || r.tag) {x.tag=1; return;}
	int ps=l.p^r.p,qs=l.q^r.q;
	x.tag=0;
	if((ps|qs)!=ps) x.tag=1;
	else x.p=l.p|r.p,x.q=l.q|r.q;
}
void out(int x) {
	for(int i=0;i<n;i++) {
		if(x&(1<<i)) printf("1");
		else printf("0");
	}
	printf("\n");
}
void tree_pre(int x,int l,int r) {
	T[x].l=l,T[x].r=r;
	if(l==r) {
		for(int i=0;i<n;i++) {
			if(s[l][i]=='?') continue;
			int add=(1<<i); T[x].p+=add;
			if(s[l][i]=='1') T[x].q+=add;
		}
		return;
	}
	int mid=(l+r)>>1;
	tree_pre(ls(x),l,mid);
	tree_pre(rs(x),mid+1,r);
	push_up(T[x],T[ls(x)],T[rs(x)]);
} 
void modify(int x,int pos) {
	if(T[x].l==T[x].r) {
		T[x].p=0,T[x].q=0;
		for(int i=0;i<n;i++) {
			if(s[pos][i]=='?') continue;
			int add=(1<<i); T[x].p+=add;
			if(s[pos][i]=='1') T[x].q+=add;
		}
		T[x].tag=0;
		return;
	}
	if(T[ls(x)].r>=pos) modify(ls(x),pos);
	if(T[rs(x)].l<=pos) modify(rs(x),pos);
	push_up(T[x],T[ls(x)],T[rs(x)]);
}
tree query(int x,int l,int r) {
	if(T[x].l>=l && T[x].r<=r) return T[x];
	tree ans;
	if(T[ls(x)].r>=l && T[rs(x)].l<=r) {
		push_up(ans,query(ls(x),l,r),query(rs(x),l,r));
		return ans;
	} 
	if(T[ls(x)].r>=l) return query(ls(x),l,r);
	else return query(rs(x),l,r);
}
int main() {
//	freopen("c.in","r",stdin);
//	freopen("c.out","w",stdout);
	scanf("%d%d%d",&n,&m,&t);
	for(int i=1;i<=m;i++) {
		scanf("%s",s[i]);
	}
	tree_pre(1,1,m);
	int all=0;
	for(int i=1;i<=t;i++) {
		int opt; scanf("%d",&opt);
		if(opt==0) {
			int l,r; scanf("%d%d",&l,&r);
			tree x=query(1,l,r);
			if(!x.tag) {
				int ans=n;
				for(int j=0;j<n;j++) {
					if(x.p&(1<<j)) ans--;
				}			
				int sum=1;
				for(int j=1;j<=ans;j++) {
					sum*=2;
				}
				all^=sum;
//				printf("%d %d\n",sum,all);
			}
		}
		else {
			int pos;
			scanf("%d",&pos);
			scanf("%s",s[pos]);
			modify(1,pos);
		}
	}
	printf("%d",all);
	return 0;
}

THE END