题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3333
题目大意:
n个数和m个操作,先输出一次初始数列的逆序对数
每次操作给一个x,表示将第x个数之后的小于等于这个数的数都提出来,从小到大排序之后依次插回原来的坑中..
每次操作完输出当前数列的逆序对数
=============================
题解:数据结构..我用了树状数组+线段树来维护...其实可以只用线段树的
//这道题是‘周六小比赛’里最容易想到的一道了qwq我也快想到了,然而莫名的否决掉了正解的想法???
设p[i],表示以第i个数为开头的逆序对数有多少个,即i后面比这个数小的有多少个
然后通过观察我们可以发现(= =),每次操作完,对于x之前的数来说p[i]是没有变的,因为无论后面的位置怎么变化还是比我小还是在我后面;对于x及x之后的没有出列的数来说也是没有变的,因为既然我没有出列,就说明我比第x个数要大,即我比出列的数都大,而他们仅仅改变了值(反正都比我小),该在我后面且比我小的个数还是没有变(依次回坑啊)。
所以每次操作就用初始逆序对sum-=p[i](i指每次出列的那些),p[i]=0(因为从小到大排序了,往后对答案没有一点贡献了)。
于是你会发现,这样每次for寻找比x小的数会超时。。。那么这个时候就要祭出线段树来维护啦~
用线段树维护当前区间最小值在哪(就是在数列中的编号),操作时就查询x到n的最小值,进行上面说的操作,因为完了之后p[i]对于以后的答案都没有贡献了但是线段树仍能找到它并做无意义且会引起错误的sum-=0(p[i]),所以在找到之后把那个最小值改成INF,更新一下线段树,这样以后都不会找到它了~
代码看下面~
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long LL;
#define maxn 501000
#define INF 0x7fffffff
struct node
{
int x,id,nw;//离散化 x-原值 id-编号 nw-离散值
}a[maxn];int len,n;
LL c[maxn],p[maxn];
bool cmp1(node x,node y) {return x.x<y.x;}
bool cmp2(node x,node y) {return x.id<y.id;}
int lowbit(int x){return x&(-x);}
struct tree
{
int l,r,lc,rc,c;//c存的就是最小值在数列的编号
}tr[maxn*2];int trlen;
void add(int x)
{
while (x<len)
{
c[x]++;
x+=lowbit(x);
}
}
LL ask(int x)
{
LL ans=0;
while (x>0)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
LL nxd()
//树状数组求逆序对的同时求出p[]
{
memset(c,0,sizeof(c));
LL ret=0;int i;
for (i=n;i>=1;i--)
{
p[i]=ask(a[i].nw-1);
ret+=p[i];
add(a[i].nw);
}return ret;
}
int mymin(int x,int y){return (a[x].nw<a[y].nw)?x:y;}
void bt(int l,int r)
{
trlen++;int now=trlen;
tr[now].l=l;tr[now].r=r;
tr[now].lc=tr[now].rc=-1;
tr[now].c=0;
if (l<r)
{
int mid=(l+r)>>1;
tr[now].lc=trlen+1;bt(l,mid);
tr[now].rc=trlen+1;bt(mid+1,r);
tr[now].c=mymin(tr[tr[now].lc].c,tr[tr[now].rc].c);
}else tr[now].c=l;
}
void updata(int now,int x)//更新一下
{
if (tr[now].l==tr[now].r) return;
int mid=(tr[now].l+tr[now].r)>>1,lc=tr[now].lc,rc=tr[now].rc;
if (x<=mid) updata(lc,x);
else updata(rc,x);
tr[now].c=mymin(tr[lc].c,tr[rc].c);
}
int query(int now,int l,int r)
{
if (tr[now].l==l && tr[now].r==r) return tr[now].c;
int mid=(tr[now].l+tr[now].r)>>1,lc=tr[now].lc,rc=tr[now].rc;
if (r<=mid) return query(lc,l,r);
else if (l>mid) return query(rc,l,r);
else return mymin(query(lc,l,mid),query(rc,mid+1,r));
}
int main()
{
//freopen("queue.in","r",stdin);
//freopen("queue.out","w",stdout);
int m,i,x,k;LL ans;
scanf("%d%d",&n,&m);
//a[0].x=a[0].nw=INF;a[0].id=0;
for (i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].id=i;
}sort(a+1,a+1+n,cmp1);
a[1].nw=1;len=1;
for (i=2;i<=n;i++)
{
if (a[i].x==a[i-1].x) a[i].nw=len;
else a[i].nw=++len;
}sort(a+1,a+1+n,cmp2);
trlen=0;bt(1,n);
ans=nxd();
printf("%lld\n",ans);
while (m--)
{
scanf("%d",&x);
int bz=a[x].nw;
//一定要开个变量存着来判断 a[x].nw可能会改变
if (bz!=INF)
{
while (1)
{
k=query(1,x,n);
if (a[k].nw>bz) break;
ans-=p[k];a[k].nw=INF;
updata(1,k);
}
}printf("%lld\n",ans);
}
return 0;
}