【原題】
3295: [Cqoi2011]動態逆序對
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 778 Solved: 263
[Submit][Status]
Description
Input
Output
Sample Input
1
5
3
4
2
5
1
4
2
Sample Output
2
2
1
樣例解釋
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。
HINT
N<=100000 M<=50000
【題外話】一直想A了這道題。真累啊!開始的想法是——倒着做,一個一個添加,套一個樹狀數組,然後每次用平衡樹去尋找。顯然,現在我只會SPLAY,而且代碼長,效率渣。今天和SYF大神一起看hzwer大神的博客,加之哲大爺的一語道破,總算會了樹狀數組套主席樹的方法。
【主要思路】首先用樹狀數組/歸併排序/歸併樹(時代的眼淚)求出總的序列中的逆序對個數。每次刪掉的時候,用樹狀數組維護位置(1--x-1或x+1--n),並且用權值線段樹維護權值信息。
【詳細算法】
①在之前的計算總的逆序對個數的時候,我們可以順帶的算出front[i]和back[i],表示第i個點之前有front[i]個數比他大,之後有back[i]個數比他小。
②起初剛開始做的時候,主席樹內部是空的。因此我們要轉化——我們本來計算的是刪掉某個數後,將會減少所剩的數列中的多少的逆序對(即對所剩的序列產生多少的貢獻);現在我們改成:刪掉某個數後,將會對主席樹中已經插入的一些節點產生多少的貢獻。這樣算出來後,用front或back減一下即可。
舉個例子。比如有數列5,4,3,2,1,已經刪掉了4和1,ans值是3。現在我們要刪掉3了。在主席樹中,已添加的節點是5和2。通過計算,5,3,2會在3的前面產生一個逆序對,在3的後面產生一個逆序對。而front[3]=2,back[3]=2,那麼我們可以知道,ans需要減去front[3]-1與back[3]-1,即最後ans只剩下了1。
③後來套的樹狀數組具體是怎麼實現的呢?比如我們要計算或更新l~r範圍內主席樹中的某個或某些權值的個數。我們可以轉化爲計算或更新1--l-1與1--r的前綴,再相減即可。而前綴是可以用樹狀數組解決的。
【附造數據程序】
#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;
int f[100005],a[100005];
int main()
{
freopen("3295.in","w",stdout);
srand((int)time(0));
int n=100000,m=50000;
printf("%d %d\n",n,m);
for (int i=1;i<=n;i++) a[i]=i;
int t;
for (int i=1;i<=1000000;i++)
{
int P=rand()%n+1,Q=rand()%n+1;
t=a[P];a[P]=a[Q];a[Q]=t;
}
for (int i=1;i<=n;i++) printf("%d\n",a[i]);
f[0]=1;
for (int i=1;i<=m;i++)
{
for (t=0;f[t];t=rand()%n+1);
f[t]=1;
printf("%d\n",t);
}
return 0;
}
【代碼】
#include<cstdio>
#include<cstring>
#define N 100005
#define L(x) (x&-x)
using namespace std;
typedef long long LL;LL ans;
struct arr{int l,r;LL sum;}a[6000000];
int front[N],back[N],c[N],root[N],pos[N],data[N],A[31],B[31];
int n,node,del,x,m,i;
inline int ask(int x){int s=0;for (;x;x-=L(x)) s+=c[x];return s;}
inline void up(int x){for (;x<=n;x+=L(x)) c[x]++;}
inline LL ask_more(int l,int r,int num)
{
if (l>r) return 0;l--;A[0]=B[0]=0;
for (int i=l;i;i-=L(i)) A[++A[0]]=root[i];
for (int i=r;i;i-=L(i)) B[++B[0]]=root[i];
l=1;r=n;LL sum=0;
while (l!=r)
{
int mid=(l+r)>>1;
if (num<=mid)
{
for (int i=1;i<=B[0];i++) sum+=a[a[B[i]].r].sum;
for (int i=1;i<=A[0];i++) sum-=a[a[A[i]].r].sum;
for (int i=1;i<=B[0];i++) B[i]=a[B[i]].l;
for (int i=1;i<=A[0];i++) A[i]=a[A[i]].l;
r=mid;
}
else
{
for (int i=1;i<=B[0];i++) B[i]=a[B[i]].r;
for (int i=1;i<=A[0];i++) A[i]=a[A[i]].r;
l=mid+1;
}
}
return sum;
}
inline LL ask_less(int l,int r,int num)
{
if (l>r) return 0;l--;A[0]=B[0]=0;
for (int i=l;i;i-=L(i)) A[++A[0]]=root[i];
for (int i=r;i;i-=L(i)) B[++B[0]]=root[i];
l=1;r=n;LL sum=0;
while (l!=r)
{
int mid=(l+r)>>1;
if (num>mid)
{
for (int i=1;i<=B[0];i++) sum+=a[a[B[i]].l].sum;
for (int i=1;i<=A[0];i++) sum-=a[a[A[i]].l].sum;
for (int i=1;i<=B[0];i++) B[i]=a[B[i]].r;
for (int i=1;i<=A[0];i++) A[i]=a[A[i]].r;
l=mid+1;
}
else
{
for (int i=1;i<=B[0];i++) B[i]=a[B[i]].l;
for (int i=1;i<=A[0];i++) A[i]=a[A[i]].l;
r=mid;
}
}
return sum;
}
inline void update(int &k,int l,int r)
{
if (!k) k=++node;a[k].sum++;
if (l==r) return;
int mid=(l+r)>>1;
if (del<=mid) update(a[k].l,l,mid);
else update(a[k].r,mid+1,r);
}
inline int Read()
{
char ch=getchar();for (;ch<'0'||ch>'9';ch=getchar());
int x=0;for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
int main()
{
n=Read();m=Read();
for (i=1;i<=n;i++)
{
data[i]=Read();pos[data[i]]=i;
ans+=(front[i]=i-1-ask(data[i]));up(data[i]);
}
memset(c,0,sizeof(c));
for (i=n;i;i--)
back[i]=ask(data[i]-1),up(data[i]);
for (i=1;i<m;i++)
{
printf("%lld\n",ans);
del=Read();x=pos[del];
ans-=front[x]-ask_more(1,x-1,del);
ans-=back[x]-ask_less(x+1,n,del);
for (;x<=n;x+=L(x)) update(root[x],1,n);
}
printf("%lld\n",ans);
return 0;
}