bzoj 4548: 小奇的糖果 && bzoj 3658: Jabberwocky(双向链表+树状数组)

3658: Jabberwocky

Time Limit: 20 Sec   Memory Limit: 1024 MB
Submit: 263   Solved: 107
[ Submit][ Status][ Discuss]

Description

平面上有n个点,每个点有k种颜色中的一个。
你可以选择一条水平的线段获得在其上方或其下方的所有点,如图所示:

请求出你最多能够得到多少点,使得获得的点并不包含所有的颜色。
   

Input

包含多组测试数据,第一行输入一个数T表示测试数据组数。
接下来T组测试数据,对于每组测试数据,第一行输入两个数n,k,分别表示点的个数和颜色数。
接下来n行每行描述一个点,前两个数z,y(lxl,lyl≤2^32-1)描述点的位置,最后一个数z(1≤z≤K)描述点的颜色。

Output

 对于每组数据输出一行,每行一个数ans,表示答案。

Sample Input

1
10 3
1 2 3
2 1 1
2 4 2
3 5 3
4 4 2
5 1 2
6 3 1
6 7 1
7 2 3
9 4 2

Sample Output

5


将问题简化:

假设线段只有可能在在坐标系的最下面or最上面,并且每个x坐标都不同

那么只要用双向链表O(n)模拟一下就好了,所有坐标点按x大小排序,第p个节点就是从小到大第p个坐标

每个节点p记录一个p.pre和p.next,分别表示在左边or右边颜色相同且离得最近的节点id,

答案就是max(max(p.next-1-p, p-1-p.pre))  (1<=p<=n),也就是两个相邻的相同颜色节点之间距离的最大值


如果存在x坐标相同的点

要先将x坐标离散化,并且上面的那个式子就会有问题,因为x坐标相同的点只有可能同时取或同时不取

这样就不能直接求p.next和p的间隔了,而是找到x坐标刚好小于p.next点以及x坐标刚好大于p点的两点之间有多少个点,求这个的最大值,因为离散化过了,这个很好处理

p和p.pre之间同理


显然这题线段没有限制,不一定在最下面or最上面

那样的话我们需要按y坐标从小到大排序(如果相同再按x排序)

求出来上面的东西之后再从下到上扫描,逐个删点,每删一个点求一次最大值

因为是双向链表,所以删点是O(1)的,并且只有这段变大的区间会影响答案

不过因为中间有些点已经消失了,所以不能直接通过相减求间隔,要用树状数组保存哪些还没有被删

别忘了还要从上往下再来一次


总复杂度O(n)+O(2nlogn)=O(nlogn)

注意这题有个坑,假设有k种颜色但是下面出现的不同颜色数量都不到k个,那么答案就是n(也就是所有点全部选中)还有bzoj3658这题的数据比较强,虽然是一模一样的题,但一开始我4548AC了,3658WA,不是long long的问题


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct
{
	LL pre;
	LL nxt;
}Line;
Line h[100005];
typedef struct Res
{
	LL x, y;
	LL id;
	LL col;
	bool operator < (const Res &b) const
	{
		if(id<b.id)
			return 1;
		return 0;
	}
}Res;
Res s[100005];
LL cnt, Hash[100005], tre[100005], temp[100005], y[100005], col[100005];
bool compx(Res a, Res b)
{
	if(a.x<b.x)
		return 1;
	return 0;
}
bool compy(Res a, Res b)
{
	if(a.y<b.y || a.y==b.y && a.x<b.x)
		return 1;
	return 0;
}
void Update(LL x, LL val)
{
	while(x<=cnt)
	{
		tre[x] += val;
		x += x&-x;
	}
}
LL Query(LL x)
{
	LL sum = 0;
	while(x)
	{
		sum += tre[x];
		x -= x&-x;
	}
	return sum;
}
int main(void)
{
	LL T, i, n, m, ans;
	scanf("%lld", &T);
	while(T--)
	{
		cnt = 0;
		scanf("%lld%lld", &n, &m);
		memset(col, 0, sizeof(col));
		for(i=1;i<=n;i++)
		{
			scanf("%lld%lld%lld", &s[i].x, &s[i].y, &s[i].col);
			s[i].id = i;
			Hash[++cnt] = s[i].x;
			col[s[i].col] = 1;
		}
		for(i=1;i<=m;i++)
		{
			if(col[i]==0)
				break;
		}
		if(i<=m)
		{
			printf("%lld\n", n);
			continue;
		}
		sort(Hash+1, Hash+cnt+1);
		cnt = unique(Hash+1, Hash+cnt+1)-(Hash+1);
		s[n+1].x = cnt+1;
		for(i=1;i<=n;i++)
			s[i].x = lower_bound(Hash+1, Hash+cnt+1, s[i].x)-Hash;

		ans = 0;
		sort(s+1, s+n+1, compx);
		memset(temp, 0, sizeof(temp));
		for(i=1;i<=n;i++)
		{
			Update(s[i].x, 1);
			h[i].pre = temp[s[i].col];
			s[i].id = i;
			temp[s[i].col] = i;
			ans = max(ans, Query(s[i].x-1)-Query(s[h[i].pre].x));
		}
		for(i=1;i<=n;i++)
			temp[i] = n+1;
		for(i=n;i>=1;i--)
		{
			h[i].nxt = temp[s[i].col];
			temp[s[i].col] = i;
			ans = max(ans, Query(s[h[i].nxt].x-1)-Query(s[i].x));
		}
		sort(s+1, s+n+1, compy);
		for(i=1;i<=n;i++)
			y[i] = s[i].id;
		sort(s+1, s+n+1);
		for(i=1;i<=n;i++)
		{
			Update(s[y[i]].x, -1);
			ans = max(ans, Query(s[h[y[i]].nxt].x-1)-Query(s[h[y[i]].pre].x));
			h[h[y[i]].nxt].pre = h[y[i]].pre;
			h[h[y[i]].pre].nxt = h[y[i]].nxt;
		}

		sort(s+1, s+n+1, compx);
		memset(temp, 0, sizeof(temp));
		for(i=1;i<=n;i++)
		{
			Update(s[i].x, 1);
			h[i].pre = temp[s[i].col];
			s[i].id = i;
			temp[s[i].col] = i;
			ans = max(ans, Query(s[i].x-1)-Query(s[h[i].pre].x));
		}
		for(i=1;i<=n;i++)
			temp[i] = n+1;
		for(i=n;i>=1;i--)
		{
			h[i].nxt = temp[s[i].col];
			temp[s[i].col] = i;
			ans = max(ans, Query(s[h[i].nxt].x-1)-Query(s[i].x));
		}
		sort(s+1, s+n+1, compy);
		for(i=1;i<=n;i++)
			y[i] = s[i].id;
		sort(s+1, s+n+1);
		for(i=n;i>=1;i--)
		{
			Update(s[y[i]].x, -1);
			ans = max(ans, Query(s[h[y[i]].nxt].x-1)-Query(s[h[y[i]].pre].x));
			h[h[y[i]].nxt].pre = h[y[i]].pre;
			h[h[y[i]].pre].nxt = h[y[i]].nxt;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值