题意:
找出一个序列里,i<j同时a[i]>j and a[j]>i的对数
题解:
问题可以转化为对每个i,找到下标区间在i+1到a[i]内的j,同时a[j]>=i的数的数量,并求和。
考虑分块,然后对每个块维护一个树状数组。分块做的还是不熟练,比赛的时候数组开小了导致没过。
分块还是很强,可以做一些线段树做不了的东西。
这题的正解是可持久化线段树,明天补。
//一对数成立的标准是 //i<j //a[i]>=j //a[j]>=i #include<bits/stdc++.h> using namespace std; const int maxn=2e5+100; typedef long long ll; int lowbit (int x) { return x&-x; } int n; int a[maxn]; int mk[205][maxn];//分块 int st[maxn]; int ed[maxn]; int pos[maxn]; int num[maxn]; void up (int p,int x) { //修改第i块的位置 for (int i=x;i<maxn;i+=lowbit(i)) mk[p][i]++; } int getsum (int p,int x) { int ans=0; for (int i=x;i;i-=lowbit(i)) ans+=mk[p][i]; return ans; } int main () { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",a+i),a[i]=min(n,a[i]); ll ans=0; int tt=n/1000; for (int i=1;i<=tt;i++) { st[i]=(i-1)*1000+1; ed[i]=i*1000; } if (ed[tt]<n) { tt++; st[tt]=ed[tt-1]+1; ed[tt]=n; } for (int i=1;i<=tt;i++) { for (int j=st[i];j<=ed[i];j++) { pos[j]=i; } } for (int i=1;i<=n;i++) { up(pos[i],a[i]); } for (int i=1;i<=n;i++) { //找小于等于a[i]的下标同时a[j]大于等于i的 //for (int j=i+1;j<=a[i];j++) { // if (a[j]>=i) ans++; //} int p1=pos[i+1]; int p2=pos[a[i]]; if (a[i]<i+1) continue; if (p1==p2) { for (int j=i+1;j<=a[i];j++) { if (a[j]>=i) ans++; } continue; } for (int j=p1;j<=p2;j++) { ans+=getsum(j,maxn)-getsum(j,i-1); } for (int j=a[i]+1;j<=ed[p2];j++) if (a[j]>=i) ans--; for (int j=st[p1];j<i+1;j++) if (a[j]>=i) ans--; } printf("%lld ",ans); }