题意:有一部电视剧有n季,每一季有ai集。问有多少对i,j存在第i季第j集也同时存在第j季第i集。
思路:核心问题还是统计对于第i季,你要统计第i行(存在多少数量,要大于i)。
线段树的维护相对而言比较暴力,树状数组的话,一开始全是1,一旦某个数过小,就会导致不构成贡献,移除就好。
tips:对于这样的题目,可以首先思考排序,这样可以省去一维操作。
线段树:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
vector<int> stk[N<<2];
int a[N];
void build(int x,int l,int r)
{
if(l==r){
stk[x].push_back(a[l]);
return;
}
for(int i=l;i<=r;i++) stk[x].push_back(a[i]);
sort(stk[x].begin(),stk[x].end());
int mid=(l+r)/2;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
}
int query(int L,int R,int x,int l,int r)
{
if(L>R) return 0;
if(L<=l && r<=R)
return stk[x].size()-(lower_bound(stk[x].begin(), stk[x].end(), L - 1) - stk[x].begin());
int ans=0;
int mid=(l+r)/2;
if(L<=mid) ans+=query(L,R,x<<1,l,mid);
if(R>mid) ans+=query(L,R,x<<1|1,mid+1,r);
return ans;
}
int main ()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
long long ans=0;
for(int i=1;i<n;i++){
ans+=query(i+1,min(a[i],n),1,1,n);
}
printf("%lld\n",ans);
return 0;
}
树状数组:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int a[N],f[N];
struct node{
int x,y;
}p[N];
bool cmp(node a,node b)
{
if(a.y==b.y) return a.x<b.x;
return a.y<b.y;
}
void add(int x,int y)
{
while(x<N){
f[x]+=y;
x+=x&-x;
}
}
int query(int x)
{
int ans=0;
while(x){
ans+=f[x];
x-=x&-x;
}
return ans;
}
int main ()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]=min(a[i],n),p[i].x=i,p[i].y=a[i],add(i,1);
sort(p+1,p+1+n,cmp);
long long ans=0;
int now=1;
for(int i=1;i<=n;i++){
while(now<=n && p[now].y<i) add(p[now++].x,-1);
ans+=query(a[i]);
if(a[i]>=i) ans--;
}
printf("%lld\n",ans/2);
return 0;
}