bzoj 4384: [POI2015]Trzy wieże 树状数组

11 篇文章 0 订阅

       显然全部相同的可以O(N)扫一遍得到,那么不妨考虑不存在某两种字母相同的最长长度。

       令x[i]为1~i中‘B’的个数,y[i]为'C'的个数,z[i]为‘S’的个数。那么某一段i+1~j,不符合题设的条件为:

       x[i]-x[j]=y[i]-y[j]或x[i]-x[j]=z[i]-z[j]或y[i]-y[j]=z[i]-z[j]。

       如果把与i相关的移到一边,然后每个点相当于一个三元组(x[i]-y[i],y[i]-z[i],z[i]-x[i]),然后如果一对u,v(u<v),使得三元组的每一个元素两两不同,那么就用v-u更新答案。

       第一维排序;然后以第二维为下标维护树状数组,存储最大值,最大值的第三维;和最大值的第三维不同的最大值。还有最小值。

       实际上可以不用树状数组,直接整体二分也可以。

       时间复杂度O(NlogN)。注意常数。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 1000000000
#define N 1000005
using namespace std;

int n,cnt,ans,tot,fst[N<<1],pnt[N],nxt[N]; struct node{ int x,y,z,id; }a[N],c[N][2];
bool cmpx(node u,node v){ return u.x<v.x; }
bool cmpy(node u,node v){ return u.y<v.y; }
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dn(node &u,int x,int y){
	if (x<u.x){
		if (y==u.y) u.x=x; else{ u.z=u.x; u.x=x; u.y=y; }	
	} else if (y!=u.y) u.z=min(u.z,x);
}
void up(node &u,int x,int y){
	if (x>u.x){
		if (y==u.y) u.x=x; else{ u.z=u.x; u.x=x; u.y=y; }	
	} else if (y!=u.y) u.z=max(u.z,x);
}
void qry(int x,int k,int y){
	for (; x; x-=x&-x){
		if (c[x][0].y==y) ans=max(ans,k-c[x][0].z); else ans=max(ans,k-c[x][0].x);
		if (c[x][1].y==y) ans=max(ans,c[x][1].z-k); else ans=max(ans,c[x][1].x-k);
	}
}
void mdy(int x,int k,int y){
	for (; x<=cnt; x+=x&-x){
		dn(c[x][0],k,y); up(c[x][1],k,y);
	}
}
void solve(){
	int i,j,k;
	for (i=1; i<=cnt; i++){
		c[i][0].x=c[i][0].z=inf; c[i][1].x=c[i][1].z=-inf;
	}
	for (i=1; i<=(n<<1|1); i++){
		for (j=fst[i]; j; j=nxt[j]){
			k=pnt[j]; qry(a[k].y-1,a[k].id,a[k].z);
		}
		for (j=fst[i]; j; j=nxt[j]){
			k=pnt[j]; mdy(a[k].y,a[k].id,a[k].z);
		}
	}
}
int main(){
	scanf("%d",&n); int i,p,t=0;
	char ch=getchar(),lastch='A';
	while (ch<'A' || ch>'Z') ch=getchar();
	for (i=1; i<=n; i++){
		if (ch==lastch) t++; else t=1; ans=max(ans,t);
		a[i]=a[i-1]; a[i].id=i;
		if (ch=='B'){ a[i].x++; a[i].z--; } else
		if (ch=='C'){ a[i].y++;  a[i].x--;} else{ a[i].z++; a[i].y--; }
		if (i<n){ lastch=ch; ch=getchar(); }
	}
	for (i=0; i<=n; i++) add(a[i].y+n+1,i);
	for (i=1; i<=(n<<1|1); i++) if (fst[i]){
		cnt++;
		for (p=fst[i]; p; p=nxt[p]) a[pnt[p]].y=cnt;
	}
	tot=0; memset(fst,0,sizeof(fst));
	for (i=0; i<=n; i++) add(a[i].x+n+1,i);
	solve();
	for (i=0; i<=n; i++) a[i].y=cnt-a[i].y+1;
	solve();
	printf("%d\n",ans);
	return 0;
}


by lych

2-16.4.2

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值