BZOJ4447: [Scoi2015]小凸解密码

这题怎么这么难读懂……而且还读错了一次……mhy的游记里写的他写的二分+线段树,然后还说可以用set,表示非常懵逼……我只会线段树暴搞,维护到左右端点的最远和次远的0区间,查询分别查左右两半的答案,再设法合并……跑得巨慢……

UPD:确实用set维护一下所有0区间就好了……我怎么想出来这个线段树的……脑抽系列……

#include<algorithm>
#include<cstdio>
#define I (J+1)
#define J (i+j>>1)
#define P (k<<1)
#define S (P^1)
using namespace std;
const int N=1e5+5;
int n,m,s,t,a[N];
char c[N];
struct node{
	int x,y,z,s1,s2,t1,t2;
	void pre(int i){
		x=y=i,z=1,s1=t1=i?-1:0,s2=t2=-1;
	}
}f[N*8];
node operator+(node a,node b){
	node c={a.x,b.y,a.z+b.z};
	int j=a.y||b.x?-1:0;
	if(b.s1==j)c.s1=a.s1,c.s2=a.s2;
	else
		c.s1=b.s1+a.z,c.s2=b.s2!=j?b.s2+a.z:a.s1;
	if(a.t1==j)c.t1=b.t1,c.t2=b.t2;
	else
		c.t1=a.t1+b.z,c.t2=a.t2!=j?a.t2+b.z:b.t1;
	return c;
}
int cal(int i){
	int s=i%n,t=(s+n-1)%n;
	if(c[s]=='+')
		return(a[s]+a[t])%10;
	return(a[s]*a[t])%10;
}
void pre(int i,int j,int k){
	if(i==j)f[k].pre(cal(i));
	else
		pre(i,J,P),pre(I,j,S),f[k]=f[P]+f[S];
}
void vary(int s,int t,int i,int j,int k){
	if(i==j)f[k].pre(t);
	else{
		if(s<I)vary(s,t,i,J,P);
		if(s>J)vary(s,t,I,j,S);
		f[k]=f[P]+f[S];
	}
}
node ask(int s,int t,int i,int j,int k){
	if(s==i&&j==t)return f[k];
	if(t<I)return ask(s,t,i,J,P);
	if(s>J)return ask(s,t,I,j,S);
	return ask(s,J,i,J,P)+ask(I,t,I,j,S);
}
void vary(int s,int t){vary(s,t,0,n*2-1,1);}
node ask(int s,int t){return ask(s,t,0,n*2-1,1);}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;++i)
		scanf("%d %c",a+i,c+i);
	pre(0,n*2-1,1);
	while(m--){
		scanf("%d%d",&s,&t);
		if(s==1){
			scanf("%d %c",a+t,c+t);
			vary(t,cal(t));
			vary(t+n,cal(t));
			vary(t+1,cal(t+1));
			if(t!=n-1)
				vary(t+n+1,cal(t+1));
		}else{
			vary(t,a[t]);
			node a=ask(t,t+(n-1)/2),b=ask(t+(n+1)/2,t+n-1);
			if(~b.t1&&(a.x||b.t1))++b.t1;
			if(~b.t2&&(a.x||b.t2))++b.t2;
			printf("%d
",a.y||b.x?max(a.s1,b.t1):max(max(a.s2,b.t2),min(a.s1,b.t1)));
			vary(t,cal(t));
		}
	}
}
原文地址:https://www.cnblogs.com/f321dd/p/6403097.html