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