https://codeforces.com/contest/1404/problem/C
私はあまりにも食べ物です、紫に戻ってリメイクしてください
重要なのは結論を見つけることです。つまり、現在の位置の前にp個の位置を削除できると仮定します。ia[i]> = pの場合、この位置も削除する必要があります。最初の1-(ia [i])を削除してください、そして現在の位置を削除し、以前に削除されていない(ia [i])-pを削除すると、iのもう1つの位置を削除できます。
次に、クエリの右端に基づいて並べ替えます。rが1ビット右にシフトされるたびに、ia [i]> = 0の場合、p ++にし、p ++を許可できない位置を検討します。各位置の現在の答えがf [ 1]、f [2] ... f [r]は、(i、r)からの答えを意味します。明らかに、f配列は増加しないため、ツリー配列を使用して、左端のf [ind] <iaを見つけます[i]の位置は、これ以降、p ++を使用できないことを意味します。indのツリー配列で+1するだけです。
接頭辞の合計は、各位置でどれだけ減算するかを示すために使用されます。ツリー配列は接頭辞の合計を維持します。最終的な答えはp-sum(l)です。つまり、1 + lでp ++が許可されない回数です。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=3e5+10;
const int inf=2e9;
int n,m,q,cnt,tot,cas;
int a[maxl],ans[maxl];
int b[maxl];
bool vis[maxl];
char s[maxl];
struct qu{int l,id;};
vector<qu> out[maxl];
inline void prework()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]=i-a[i];
}
int x,y;
for(int i=1;i<=q;i++)
{
scanf("%d%d",&x,&y);
out[n-y].push_back(qu{1+x,i});
}
}
inline int find(int n,int k,int p)
{
int now=0,cnt=p;
for(int i=log2(n);i>=0;i--)
if(now+(1<<i)<=n)
{
now+=1<<i;cnt-=b[now];
if(cnt<k)
cnt+=b[now],now-=1<<i;
}
return now+1;
}
inline void add(int i,int x)
{
while(i<=n)
{
b[i]+=x;
i+=i&-i;
}
}
inline int sum(int i)
{
int ret=0;
while(i)
{
ret+=b[i];
i-=i&-i;
}
return ret;
}
inline void mainwork()
{
int p=0,ind;
for(int r=1;r<=n;r++)
{
if(a[r]>=0)
{
ind=find(r,a[r],p);
++p;
add(ind,1);
}
for(qu d:out[r])
ans[d.id]=p-sum(d.l);
}
}
inline void print()
{
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
}
int main()
{
int t=1;
//scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
prework();
mainwork();
print();
}
return 0;
}