bzoj 3295 [Cqoi2011]动态逆序对

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 4785   Solved: 1613
[ Submit][ Status][ Discuss]

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
 

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

N<=100000 M<=50000

Source





【分析】
吼题啊...
位置(w)一个维度,权值(a)一个维度,把位置排序,权值CDQ一下,时间线(t)用树状数组统计一下
题目就是要求对于一个t0,满足t<t0,w<w0,a>a0的个数和t<t0,w>w0,a<a0的个数和...这两个做两遍CDQ分治就好了...(类似陌上花开QAQ)
昨晚肝了半晚上没肝出来...诶蠢
对了我写的CDQ自带大常数(QAQ 2000ms+)



【代码】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=100005;
int n,m,T,num;
int to[mxn],c[mxn],tim[mxn];
ll res[mxn];
struct node
{
    int a,w,t;
}p[mxn],tmp[mxn];
inline bool comp(node x,node y)
{
    return x.w<y.w;
}
inline int lowbit(int x) {return x&-x;}
inline void add(int x,int v)
{
	for(int i=x;i<=n;i+=lowbit(i))
	  if(tim[i]!=T) c[i]=v,tim[i]=T;
	  else c[i]+=v; 
} 
inline int getsum(int x)
{
	int sum=0;
	for(int i=x;i>=1;i-=lowbit(i))
	  if(tim[i]==T) sum+=c[i];
	return sum;
}
inline void CDQ(int l,int r)
{
    if(l==r) return;
    int i,j,mid=l+r>>1,l1=l,l2=mid+1;
    fo(i,l,r)
      if(p[i].a<=mid) tmp[l1++]=p[i];
      else tmp[l2++]=p[i];
    memcpy(p+l,tmp+l,sizeof(p[0])*(r-l+1));
    CDQ(l,mid);T++;j=l;
    fo(i,mid+1,r)
    {
    	for(j;j<=mid && p[j].w<p[i].w;j++)
    	  add(p[j].t,1);
    	res[p[i].t]+=getsum(p[i].t);
	}
	CDQ(mid+1,r);
	l1=l,l2=mid+1;
	fo(i,l,r)
	{
		if((comp(p[l1],p[l2]) || l2>r) && l1<=mid)
		  tmp[i]=p[l1++];
		else 
		  tmp[i]=p[l2++];
	}
	memcpy(p+l,tmp+l,sizeof(p[0])*(r-l+1));
}
int main()
{
    int i,j,c;
    scanf("%d%d",&n,&m);
    for(i=n;i>=1;i--)
      scanf("%d",&p[i].a),p[i].w=i,to[p[i].a]=i;
    for(i=m;i>=1;i--) 
    {
        scanf("%d",&c);
        p[to[c]].t=i-m+n;
    }
    fo(i,1,n) if(!p[i].t) p[i].t=++num;
    sort(p+1,p+n+1,comp);
    CDQ(1,n);
    fo(i,1,n) p[i].w=n-p[i].w+1,p[i].a=n-p[i].a+1;
    sort(p+1,p+n+1,comp);
    CDQ(1,n);
    fo(i,1,n) res[i]+=res[i-1];
    for(i=n;i>=n-m+1;i--) printf("%lld\n",res[i]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值