BZOJ 3658: Jabberwocky 【双向链表+树状数组】

题目描述:

平面上有n个点,每个点有k种颜色中的一个。
你可以选择一条水平的线段获得在其上方或其下方的所有点,如图所示:
在这里插入图片描述
请求出你最多能够得到多少点,使得获得的点并不包含所有的颜色。
n,k<=100000

题目分析:

不包含所有的颜色 → \rarr 保证一种颜色不选,其它任意
考虑两个颜色相同的横坐标相邻的点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0) ( x 1 , y 1 ) (x_1,y_1) (x1,y1),那么横坐标在 [ x 0 + 1 , x 1 − 1 ] [x_0+1,x_1-1] [x0+1,x11]的点就可以全部得到。
这时候相当于选择的线段无限低,选择它上方的点。

把这条线往上,一次删除一行,删除的时候一个点的时候找到它上方的跟它颜色相同的前驱 p r e pre pre和后继 n e x t next next(没有就是0和n+1),计算 [ p r e + 1 , n e x t − 1 ] [pre+1,next-1] [pre+1,next1]之间的点数(这条线下方的点已经删掉了)。

所以就是维护前驱后继,统计区间点数。
前者可以用双向链表(先链起来,删除点的时候把它的pre和next接上)或者动态开点线段树。 后者可以树状数组。

Code:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define maxn 100005
#define LL long long
using namespace std;
inline void read(int &a){
    char c;bool f=0;
	while(!isdigit(c=getchar())) if(c=='-') f=1;
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
	if(f) a=-a;
}
int T,n,m,pre[maxn],nxt[maxn],last[maxn],ans,X[maxn];
int arr[maxn];
struct node{
	int x,y,c,id;
}a[maxn];
bool cmpx(const node &a,const node &b){return a.x<b.x;}
bool cmpy(const node &a,const node &b){return a.y<b.y;}
void update(int i,int d){for(;i<=n;i+=i&-i) arr[i]+=d;}
int getsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
inline void calc(int l,int r){if(l<=r) ans=max(ans,getsum(r)-getsum(l-1));}
void solve(){
	memset(arr,0,sizeof arr);
	memset(last,0,sizeof last);
	for(int i=1;i<=n;i++){
		update(X[a[i].id=i]=a[i].x,1);
		pre[i]=last[a[i].c],nxt[i]=n+1;
		if(pre[i]) nxt[pre[i]]=i;
		last[a[i].c]=i;
		calc(X[pre[i]]+1,X[i]-1);
	}
	X[n+1]=n+1;
	for(int i=1;i<=m;i++) calc(X[last[i]]+1,n);
	sort(a+1,a+1+n,cmpy);
	for(int i=1,j=1;i<=n;i=j){
		while(j<=n&&a[j].y==a[i].y) update(a[j++].x,-1);
		for(int k=i;k<j;k++){
			int o=a[k].id;
			nxt[pre[o]]=nxt[o];
			pre[nxt[o]]=pre[o];
			calc(X[pre[o]]+1,X[nxt[o]]-1);
		}
	}
}
int main()
{
	read(T);
	while(T--){
		read(n),read(m),ans=0;
		for(int i=1;i<=n;i++) read(a[i].x),read(a[i].y),read(a[i].c);
		int cur=1;
		sort(a+1,a+1+n,cmpx);
		for(int i=1;i<=n;i++){
			a[i].x=cur;
			if(a[i+1].x!=a[i].x) cur++;
		}
		solve();
		for(int i=1;i<=n;i++) a[i].y=-a[i].y;
		sort(a+1,a+1+n,cmpx);
		solve();
		printf("%d\n",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值