BZOJ 3295: Cqoi2011动态逆序对

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88344473

题目
凭吊我以前没过的3KB代码和现在的1.2KB代码,哎。

题解:
首先我们比起删除更喜欢插入,所以我们可以把m次删除操作倒过来看作m次插入操作。
然后显然我们就需要求每次插入一个数,求出插入这个数增加了多少逆序对即可。(思路清晰)
那么这就是一个动态二维数点问题直接上树状数组套主席树就结束了。
可是这怎么1.2KB呢?
考虑用CDQ分治。
假设每个数的位置为 a i a_i ,数的大小为 b i b_i ,插入时间为 c i c_i
那么对于 a i < a j b i > b j a_i<a_j且b_i>b_j 的每对数都会给 c i c_i 较大的数的插入增加逆序对数+1。
然后可以对a,b或c分治。
对c分治是最简单的,因为肯定是前半区间对后半区间贡献,
但如果对a,b分治,那就需要两个树状数组了,因为后半区间也可以对前半区间贡献。。。。。。

AC Code(加了读优所以变长了)

#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;

char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
inline void read(int &res)
{
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

int n,m,tr1[maxn],tr2[maxn],a[maxn],b[maxn],pt[maxn],tmp[maxn],c[maxn],loc[maxn];
LL ans[maxn];

inline void upd(int *tr,int now,int val)
{for(;now<=n;now+=now&-now) tr[now]+=val; }
inline int qsum(int *tr,int now,int ret=0)
{for(;now;now-=now&-now) ret+=tr[now]; return ret; }

void Solve(int l,int r)
{
	if(l >= r) return;
	int mid = (l+r) >> 1 , i = l , j = mid+1 , k = l;
	Solve(l,mid),Solve(mid+1,r);
	for(;i<=mid || j<=r;)
	{
		if((i<=mid) && ((j>r) || b[c[i]]<=b[c[j]])) 
		{
			pt[c[i]] += qsum(tr2,a[c[i]]);
			upd(tr1,a[c[i]],1);
			tmp[k++] = c[i++];
		}
		else 
		{
			pt[c[j]] += i-l - qsum(tr1,a[c[j]]);
			upd(tr2,a[c[j]],1);
			tmp[k++] = c[j++];
		}
	}
	
	for(int i=l;i<=r;i++)
	{
		if(i <= mid) upd(tr1,a[c[i]],-1);
		else upd(tr2,a[c[i]],-1);
		c[i] = tmp[i];
	}
}

int main()
{
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]),c[i]=i,loc[a[i]]=i;
	for(int i=1;i<=m;i++)
	{
		int x;
		read(x);x = loc[x];
		b[x] = m-i+2;
	}
	for(int i=1;i<=n;i++) if(!b[i]) b[i] = 1;
	Solve(1,n);
	for(int i=1;i<=n;i++) ans[b[i]]+=pt[i];
	for(int i=2;i<=m+1;i++) ans[i]+=ans[i-1];
	for(int i=m+1;i>=2;i--) printf("%lld\n",ans[i]);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88344473