[CSP-S模拟测试]:购物(柯朵莉树)

题目描述

$visit_world$有一个商店,商店里卖$N$个商品,第$i$个的价格为$a[i]$我们称一个正整数$K$是美妙的,当且仅当我们可以在商店里选购若干个商品,使得价格之和落在区间$[K,2K]$中。
问:有多少个美妙的数。


输入格式

第一行一个整数$N$。
接下来一行$N$个整数,描述数组$a[]$。


输出格式

输出一行一个整数,表示答案。


样例

样例输入:

3
1 2 3

样例输出:

6


数据范围与提示

样例解释:

可以证明$1\leqslant K\leqslant 6$都是美妙的,除此之外的数都不是美妙的。

数据范围:

子任务$1$($30$分):$N\leqslant 100,a_i\leqslant 100$。
子任务$2$($20$分):$N\leqslant 100000,a_i\leqslant 20$。
子任务$3$($20$分):$N\leqslant 3,a_i\leqslant 10^9$。
子任务$4$($30$分):$N\leqslant 10^5,a_i\leqslant 10^9$。


题解

正解不会,今天刚学柯朵莉树,于是就打了它(可惜考试的时候并不会……)

基本上就是一道柯朵莉树的板子题,不妨就拿它来讲一下柯朵莉树吧~

柯朵莉树的原理很简单,就是不断往里面添加区间,添加完之后再进行合并(有交集的两个区间合并),用$set$维护这些区间就好了。

来讲几个细节:

  $\alpha.$添加区间的时候,如果要同时添加多个区间(比如这道题),需要先枚举$set$里所有的原区间,生成新的区间并将这些区间放到$vector$里,如果直接放进去会造成死循环(当然也是错的)。

  $\beta.$合并时与前一个区间比较,会更方便处理(细节看代码)。

  $\gamma.$合并之后注意$it$指针的位置,可以用二分查找……

时间复杂度:$\Theta(n\log\log n)$(随机数据)。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int N;
int a[100001];
set<pair<int,long long>>s;
vector<pair<int,long long>>v;
long long ans;
void add(int x)
{
	for(auto it=s.begin();it!=s.end();it++)
		v.push_back(make_pair((*it).first+x,(*it).second+2*x));
	while(v.size())
	{
		s.insert(v.back());
		v.pop_back();
	}
}
void split()
{
	for(auto it=s.begin();;)
	{
		auto ti=it;it++;
		if(it==s.end())break;
		if((*ti).second>=(*it).first)
		{
			pair<int,long long> flag=make_pair((*ti).first,(*it).second);
			s.erase(it);s.erase(ti);s.insert(flag);
			it=s.lower_bound(flag);
		}
	}
}
int main()
{
	scanf("%d",&N);
	s.insert(make_pair(0,0));
	for(int i=1;i<=N;i++)
	{
		scanf("%d",&a[i]);
		add(a[i]);
		split();
	}
	for(auto it=s.begin();it!=s.end();it++)
	{
		if(!(*it).first)continue;
		ans+=((*it).second+1)/2-((*it).first-1)/2;
	}
	printf("%lld",ans);
	return 0;
}

rp++

posted @ 2019-10-17 08:46  HEOI-动动  阅读(216)  评论(3编辑  收藏  举报