Codeforces Global #2 D. Frets On Fire (二分,前缀和)

题目连接: https://codeforces.com/problemset/problem/1119/D

题目大意: 有一个n行,1e18+1列的数组A,给出一个1行n列的数组S, A[I][J]=S[I]+J ,有q组询问,给出l,r 问n行l~r列数组A中有多少不同的元素.

通过样例不难发现,问题等价于将S数组每位都加上0~(l-r)后有多少不同的数字,我们将S排序后不难发现相邻两位的差为该位最多能增加几次不重复,最大的数则能一直增加.

例如样例中 S=3 1 4 1 5 9 那么排序后为 1 1 3 4 5 9 ,其差值数组(只考虑差值>1的情况,因为差值<=1的话,后一列的该元素一定在这次就出现了)ha=2 4,将ha中元素-1则为每个元素能改变的次数, ha=1 3我们能记录下S中不同值的个数,就是不增加S拥有的不同的元素.
对于增加X后有多少个不同的元素,我们需要二分数组ha中x的位置
例如x=2, 我们需要统计s+0,s+1,s+2中有多少不同元素,二分x找到比x大的第一个元素i, i 前面的数都能变化完,用前缀和来统计,i后面的数都只能变化x次,最后每次询问的答案就是 不改变数组时S中不同元素的个数+(r-l)+sum[i-1]+(ha.size()-i)*(r-l) 第一个r-l代表最大的那个数字能改变r-l次

可能讲不太清楚 看代码吧 需要注意下没有差值的情况(不然会RE)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<stdlib.h>
#include<algorithm>
#include<time.h>
#define bug1(g) cout<<"test: "<<g<<endl
#define bug2(g,i) cout<<"test: "<<g<<" "<<i<<endl
#define bug3(g,i,k) cout<<"test: "<<g<<" "<<i<<" "<<k<<endl
using namespace std;
typedef unsigned long long ll;
ll s[3000005]; 
ll l,r;
vector<ll>ha;
ll sum[3000005]; //记录前缀和
ll q,n;
int main()
{
  // ios::sync_with_stdio(0);
   cin>>n;
   for(int i =0;i<n;i++)
       cin>>s[i];
   sort(s,s+n);
   ha.clear();
   ll maxx=1;
   for(int i =1;i<n;i++)
   {
       if(s[i]!=s[i-1]) ++maxx;
       if(s[i]-s[i-1]<=1) continue;
       ha.push_back(s[i]-s[i-1]-1);
   }

   if(ha.size()!=0) //特判ha时候为0
   {
      sort(ha.begin(),ha.end());
      sum[0]=ha[0];
   }
   for(int i =1;i<ha.size();i++)
   {
       sum[i]=sum[i-1]+ha[i];
   }

   cin>>q;
   while(q--)
   {
       cin>>l>>r;
       ll ans=maxx+r-l;
       ll haa=ha.size();
       //bug3(maxx,ans,r-l);
       if(haa){
       ll i =upper_bound(ha.begin(),ha.end(),r-l)-ha.begin(); //up找到比r-l大的第一个元素
     // cout<<i<<endl;
       if(i>=1)
        ans+=sum[i-1]+(haa-i)*(r-l);
       else ans+=(haa-i)*(r-l);}
       //bug3(sum[i-1],ha.size()-i,r-l);}
       cout<<ans<<endl;
   }
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值