NOIP的快乐

 

终于到了一年一度的NOIP比赛了,多么令人期待和兴奋的一天!其实,人们最高兴的还不是遇见老朋友,而是结交新朋友。可是结交新的朋友就需要很多时间,而除了考试之外时间并不多。例如小L,他在NOIP的入口处等待开门时,决定趁机和其它市县的牛们多套近乎。可是队伍太长,且人们都很自觉的站成仅仅一列,而小L又很想多交不同地方的朋友,因此小L想知道他在哪一个区域内可以结交到最多的不同地方的朋友。当然,这个区域不能太大,否则还没考试他就累死了。
现在有n个人,题目给出了他们每个人所在市县的编号。他们站在一个从左向右的队伍中。小L不在队列中。他想找到一个长度不超过D的区域,使他能够找到最多的不同地方的朋友。要求输出能找到的朋友所在不同市县的最大数和找到这些朋友的最小区间长度。比如在整个队伍内他按从左向右顺序找到了3个A地朋友,1个B地朋友,1个C地朋友。假设D=5,那么不同市县的最大数为3(A地、B地、C地),最小区间长度为3(只须结交A地的最右面的一个人即可得到最大市县数3,因此区间长度不是5而是3)。假设在队伍内的人他都还没有结交。

输入

第一行为两个正整数n,D。分别表示队伍人数和他想找到的最长的区间长度。
接下来的n行,每行有一个整数,表示每个人所在市县的编号(从左向右)。

输出

输出数据为一行,这行有两个整数,用空格分开,按顺序分别代表能找到的朋友所在不同市县的最大数和找到这些朋友的最小区间长度。

样例输入 

5 4
1
1
1
2
3

样例输出 

3 3

提示

对于 100% 的数据,保证5<=n<=1000000, 1<=D<=n, 所有市县编号不超过32767。

#include<iostream>
#include<bits/stdc++.h>
#include<algorithm>
using namespace std;
typedef long long ll;
deque<ll> lis;//选用deque,可同时对头和尾进行修改
ll n,d,r;
ll flag[32800];//统计容器对应元素的个数
ll flag1[32800];//找到一区段后统计该区段内个元素对应个数;也可不定义,清空flag[]即可
//在此题中数据不超过32767,可用桶排思想记录个数
ll st[1000010];//存放所有数据
ll num,lenth,lenth1,ans,ans1;

ll read() {//快读
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*f;
}

int main() {
	n=read();
	d=read();
	
	for(ll i=1; i<=n; i++)	st[i]=read();
	
	for(ll i=1; i<=n; i++) {
		num=st[i];
		if(lenth1==0) {//容器为空,不必考虑重复元素
			ans1++;
			lenth1++;
			flag[num]++;
			lis.push_back(num);
		}
		else {
			ll ls=lis.front();
			if(lenth1<d) {//容器未满,只需将元素添至容器尾,判断当前加入元素是否重复
				flag[num]++;
				lenth1++;
				lis.push_back(num);
				if(flag[num]==1)	ans1++;
			}
			else {//容器满,考虑头的删除,尾的加入;删除和加入可能影响元素种类
				flag[ls]--;
				if(flag[ls]==0)	ans1--;
				lis.pop_front();
				lis.push_back(num);
				flag[num]++;
				if(flag[num]==1)	ans1++;
			}
		}
		if(ans<ans1) {//最大种类值,保存右坐标和长度
			ans=ans1;
			lenth=lenth1;
			r=i;
		}
	}
	
	lis.clear();//已寻得最大种类,清空容器
	
	for(int i=r,len=lenth1; len>0; i--,len--) {
		lis.push_back(st[i]);
		flag1[st[i]]++;
	}
	//将目标区域段元素存入容器;正反向无妨;即亦可于上段中保存左值
	while(flag1[lis.front()]>1) {
		flag1[lis.front()]--;
		lis.pop_front();
		lenth--;
	}
	
	while(flag1[lis.back()]>1) {
		flag1[lis.back()]--;
		lis.pop_back();
		lenth--;
	}
	//左右双向删减
	cout<<ans<<' '<<lenth;
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值