一、题目
二、解法
一看就是折半搜索,但是左边要排序右边要二分的, O ( 2 20 × q log ) O(2^{20}\times q\log) O(220×qlog)
这种题往往就是卡掉排序的复杂度,我们可以考虑用归并排序来卡。
假设我们维护的是一个有序数组,那么我们加一个 v v v 的时候这个数组就变成了两个。一个是原来的数组,一个是每个元素都加上了 v v v 的数组,那么这两个数组就可以用归并,所以第一部分的复杂度变成 O ( 2 s ) O(2^s) O(2s)
第二部分其实也可以用归并,一共有 q 2 n − s q2^{n-s} q2n−s 个询问,每次加入一个 v v v 时可以用相同的方法归并起来,复杂度是 O ( q 2 n − s ) O(q2^{n-s}) O(q2n−s),然后我们用一个指针就可以一次处理所有询问。
那么当 2 s = q 2 40 2^s=\sqrt{q2^{40}} 2s=q240 时最优,时间复杂度是 O ( q 2 40 ) O(\sqrt{q2^{40}}) O(q240 ),为了卡空间我干了一些好事
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int M = (1<<25)+5;
const int K = 1e14;
const int K2 = 1e18;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,q,o,a[M],b[M],c[M],p[M],v[45],ans[505];
struct node
{
int x,id,fl;
node(int X=0,int I=0,int F=0) : x(X) , id(I) , fl(F) {}
bool operator < (const node &b)
{
return x<b.x;
}
}s[1005];
void merge(int *a,int *b,int &m1,int m2)
{
int i=1,j=1,t=0;
while(i<=m1 && j<=m2)
{
if(a[i]%K<=b[j]%K) c[++t]=a[i],i++;
else c[++t]=b[j],j++;
}
while(i<=m1) c[++t]=a[i],i++;
while(j<=m2) c[++t]=b[j],j++;
m1=t;
for(int i=1;i<=m1;i++)
a[i]=c[i];
}
signed main()
{
n=read();q=o=read();
k=min(25ll,n);
for(int i=1;i<=n;i++)
v[i]=read();
a[++m]=0;
for(int i=1;i<=k;i++)
{
for(int j=1;j<=m;j++)
b[j]=a[j]+v[i];
merge(a,b,m,m);
}
for(int i=1;i<=q;i++)
{
int l=read()-1,r=read();
s[2*i-1]=node(l,i,K2);
s[2*i]=node(r,i,0);
}
q=q<<1;
sort(s+1,s+1+q);
for(int i=1;i<=q;i++)
p[i]=s[i].x+K*s[i].id+s[i].fl;
for(int i=k+1;i<=n;i++)
{
int t=0;
for(int j=1;j<=q;j++)
if((p[j]%K)-v[i]>=0)
b[++t]=p[j]-v[i];
merge(p,b,q,t);
}
for(int i=1,j=1;i<=q;i++)
{
while(j<=m && (p[i]%K)>=a[j]) j++;
int id=(p[i]%K2)/K,tg=p[i]/K2;
if(tg) ans[id]-=(j-1);
else ans[id]+=(j-1);
}
for(int i=1;i<=o;i++)
printf("%lld\n",ans[i]);
}