zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:购物(柯朵莉树)

    题目描述

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


    输入格式

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


    输出格式

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


    样例

    样例输入:

    3
    1 2 3

    样例输出:

    6


    数据范围与提示

    样例解释:

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

    数据范围:

    子任务$1$($30$分):$Nleqslant 100,a_ileqslant 100$。
    子任务$2$($20$分):$Nleqslant 100000,a_ileqslant 20$。
    子任务$3$($20$分):$Nleqslant 3,a_ileqslant 10^9$。
    子任务$4$($30$分):$Nleqslant 10^5,a_ileqslant 10^9$。


    题解

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

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

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

    来讲几个细节:

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

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

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

    时间复杂度:$Theta(nloglog 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++

  • 相关阅读:
    PID控制原理和算法
    Android 按钮长按下去重复执行某个动作,放开后停止执行动作
    最小二乘法拟合非线性函数及其Matlab/Excel 实现
    Android 基于蓝牙的方向控制器
    Android/Java 中的 String, StringBuffer, StringBuilder的区别和使用
    input 只允许输入数字
    java scoket编程
    RBAC权限管理
    整理大型网站架构必知必会的几个服务器知识
    Java中Class<T>与Class<?>的区别
  • 原文地址:https://www.cnblogs.com/wzc521/p/11689836.html
Copyright © 2011-2022 走看看