题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3295
3295: [Cqoi2011]动态逆序对
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 7178 Solved: 2548
[Submit][Status][Discuss]
Description
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删
除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数
Input
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。
以下n行每行包含一个1到n之间的正整数,即初始排列。
以下m行每行一个正整数,依次为每次删除的元素。
N<=100000 M<=50000
Output
输出包含m行,依次为删除每个元素之前,逆序对的个数。
Sample Input
5 4
1
5
3
4
2
5
1
4
2
Sample Output
5
2
2
1
思路:这是一道十分经典的题目。可以用CDQ分治做,也可以用树套树做。不过做树套树身体会被掏空的,还是CDQ好了。。。
之前一直不太了解CDQ分治,虽然听说过它的威名,却一直没能学会。今天突击了一下,算是刚入了点门吧(可能连门都没入)
如同网上大佬们所说的,一般三维的CDQ问题,第一位排序,第二维分治,第三位数据结构(BIT)。。。
本蒟蒻就照着这个做了。不过之前一直没能理解别人的代码中额外开一个数组记录是什么意思,后来在汪聚聚的讲解下明白了这是归并操作(话说我是不是学了个假的分治。。。)
这道题具体思路可以看: http://www.cnblogs.com/lidaxin/p/5255440.html
主要思想就是将每个点抽象成一个三元组(位置,数,时间戳)。而上面的做法的巧妙之处在于将删点转化为加点(时间戳反向就行了)。所以这道题变成了第一位排序时间戳,第二维CDQ位置,第三维用BIT位数它前面的数。由于每个点要统计前面和后面的点,所以要跑两次CDQ。。。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
typedef struct
{
ll x;
ll p;
ll T;
} C;
C c[MAXN];
int pos[MAXN];
C res[MAXN];
ll tot=0;
ll mx;
int n,m;
bool cmp1(C a, C b)
{
return a.T<b.T;
}
bool cmp2(C a,C b)
{
return a.p<b.p;
}
ll sum[MAXN];
ll ans[MAXN];
void add(int p, ll x){
while(p) sum[p] += x, p -= p & -p;
}
ll ask(int p){
ll res = 0;
while(p <= mx) res += sum[p], p += p & -p;
return res;
}
void solve(int l,int r)
{
if(l==r)
return ;
int m=(l+r)>>1;
solve(l,m);
solve(m+1,r);
int ptr=l;
tot=l-1;
for(int i=m+1;i<=r;i++)
{
while(ptr<=m&&c[ptr].p<c[i].p)
{
add(c[ptr].x,1);
res[++tot]=c[ptr];
ptr++;
}
res[++tot]=c[i];
ans[c[i].T]+=ask(c[i].x+1);
}
for(int i=l;i<ptr;i++)
{
add(c[i].x,-1);
}
for(int i=ptr;i<=m;i++)
res[++tot]=c[i];
for(int i=l;i<=r;i++)
{
c[i]=res[i];
}
}
int main()
{
ll tem;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&c[i].x);
c[i].p=i;
pos[c[i].x]=i;
mx=max(mx,c[i].x);
}
for(int i=1;i<=m;i++)
{
scanf("%lld",&tem);
c[pos[tem]].T=m-i+1;
}
sort(c+1,c+1+n,cmp1);
solve(1,n);
for (int i=1;i<=n;++i)
{
c[i].p*=-1;
c[i].x=n-c[i].x+1;
}
sort(c+1,c+1+n,cmp1);
solve(1,n);
for(int i=1;i<=n;i++)
{
ans[i]+=ans[i-1];
}
for(int i=1;i<=m;i++)
{
printf("%lld\n",ans[m-i+1]);
}
return 0;
}