1. 程式人生 > >三校聯考20181024T3 統計count

三校聯考20181024T3 統計count

題意: 題面 子任務

題解: 話說T2和T3的位置是故意的吧?還有std的程式碼真醜(就像隔壁蘇珊嬸嬸做的蘋果派一樣)(基本上全機房的人都這麼想)。 先說60%的資料,直接暴力列舉要取出的位置,暴力排序,然後每一次用樹狀陣列或歸併排序求逆序對數即可。 對於100%的資料,我們可以維護一個f[i]f[i]j>i,a[j]<a[i]j>i,a[j]<a[i]jj的個數,那麼逆序對數就是f[i]\sum f[i]。可以發現,經過一次操作後,被安排的位置的f[i]f[i]就變成了0,而其他位置不變。因為如果一個位置被安排了,那麼它前面的數後面還是那些數,後面的比它小的位置也會被安排,而比它大的位置不變,而且被安排到它後面的仍然比它小。因為f[i]變成0之後就對答案沒有貢獻了,所以我們就不用再維護被安排過的位置了。 用線段樹維護一下一個區間中f[i]的和與a[i]的最小值,操作時,如果區間的最小值

ajk\le a_{j_k},就遞迴下去直到葉節點。如果某個位置被安排了,就把這個位置上的f[i]變為0,最小值變為INF。

程式碼:

#include<cstdio>
#include<algorithm>
#define maxn 200005
#define INF (LL)1e16
#define LL long long
using namespace std;
int n,m,a[maxn],tmp[maxn],bit[maxn],f[maxn],cnt;
inline int lowbit(int x) { return x&(-x); }
void
Add(int x,int d) { for(;x<=n;x+=lowbit(x)) bit[x]+=d; } int Query(int x) { int res=0; for(;x;x-=lowbit(x)) res+=bit[x]; return res; } struct node { LL f,minx; } tree[maxn*4]; void Pushup(int i) { tree[i].f=tree[i<<1].f+tree[i<<1|1].f,tree[i].minx=min(tree[i<<1].minx,tree[i<<
1|1].minx); } void Build(int i,int l,int r) { if(l==r) { tree[i].f=f[l],tree[i].minx=a[l]; return; } int mid=(l+r)>>1; Build(i<<1,l,mid); Build(i<<1|1,mid+1,r); Pushup(i); } void Modify(int i,int l,int r,int p,int d) { if(tree[i].minx>d) return; if(l==r) { tree[i].minx=INF,tree[i].f=0; return; } int mid=(l+r)>>1; if(p<=mid) Modify(i<<1,l,mid,p,d); Modify(i<<1|1,mid+1,r,p,d); Pushup(i); } bool Query(int i,int l,int r,int p) { if(l==r) return tree[i].f!=0; int mid=(l+r)>>1; if(p<=mid) return Query(i<<1,l,mid,p); else return Query(i<<1|1,mid+1,r,p); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=n;i;i--) { f[i]=Query(a[i]-1); Add(a[i],1); } Build(1,1,n); printf("%lld",tree[1].f); while(m--) { int jk; scanf("%d",&jk); if(Query(1,1,n,jk)) Modify(1,1,n,jk,a[jk]); printf(" %lld",tree[1].f); } }