BZOJ3416: Poi2013 Take-out

111 篇文章 0 订阅
2 篇文章 0 订阅

题目大意:有n块砖,其中白色是黑色的k倍,求一个消除序列,满足以下条件:

                  每次消除k+1个砖,其中k块白色,1块黑色,并且这k+1块砖从开始到结束,中间不能路过已经消除过的砖

                  数据保证有解

 

这真是一个悲伤的故事,首先我把题想偏了,以为是只能每次取两边,写了一发WA了

然后网上没有题解,就很悲催的去看了波兰文的题解,里面一大堆百度翻译都翻译不出来的波兰语,就是如同什么“好数”“谈笑风声”之类的定义

幸亏我机智的看懂了他说的一部分话,才发现题意想错了,具体做法其实很简单

 

我们把这个序列的白色看成1,黑色看成-k,这样的话可以求一个前缀和,最近的a[i]相同的两个点之间的距离一定恰好为k+1

那我们就可以弄一个栈,按顺序把前缀和推进去,每当新进来的元素和之前栈中某一个元素权值相同,就把新来的元素连同两个元素之间的元素全部弹出,作为一次消除操作

这样的话倒序的消除序列一定是合法的,我就不证了

时间复杂度O(N)

 

然后还有一件事,POI官方题解时间复杂度O(NlogK)我也不知道是什么鬼,有兴趣可以去看看 POI2013波兰文题解 第82页

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 1000010
using namespace std;
bool la[N<<1];
char s[N];
int a[N];
int q[N],t;
vector<int>ans[N];
int main()
{
	int n,k;
	scanf("%d%d%s",&n,&k,s+1);
	int i,j,x,y;
	for(i=1;i<=n;i++)
	{
		if(s[i]=='c') a[i]=a[i-1]-k;
		else a[i]=a[i-1]+1;
	}
	int cnt=0;
	la[n]=true;
	for(i=1;i<=n;i++)
	{
		a[i]+=n;
		if(la[a[i]])
		{
			cnt++;
			for(j=t-k+1;j<=t;j++)
			{
				ans[cnt].push_back(q[j]);
				la[a[q[j]]]=false;
			}
			ans[cnt].push_back(i);
			t-=k;
		}
		else
		{
			t++;q[t]=i;
			la[a[i]]=true;
		}
	}
	for(i=cnt;i>=1;i--)
	{
		for(j=0;j<ans[i].size();j++)
		printf("%d ",ans[i][j]);
		puts("");
	}
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值