• 【Luogu1393】动态逆序对(CDQ分治)


    【Luogu1393】动态逆序对(CDQ分治)

    题面

    题目描述

    对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i < j的有序对(i,j)的个数。你需要计算出一个序列的逆序对组数及其删去其中的某个数的逆序对组数。

    输入输出格式

    输入格式:

    第一行,两个数n,m,表示序列中有n个数,要删去m个数

    第二行n个数,表示给定的序列。

    第三行m个数,第i个数di表示要删去原序列中的第di个数。

    输出格式:

    一行m+1个数。第一个数表示给定序列的逆序对组数,第i+1个数表示删去第di个数后序列的逆序对组数(删去的数不再恢复)

    输入输出样例

    输入样例#1:

    6 3
    5 4 2 6 3 1
    2 1 4

    输出样例#1:

    11 7 4 2

    说明

    对于20%的数据,n≤2500

    对于另30%的数据,m=0

    对于100%的数据,n≤40000,m≤n/2,且保证第二行n个数互不相同,第三行m个数互不相同

    题解

    之前不是说过要写一遍CDQ分治吗??
    在这里说的
    可是,当你把上面的代码兴高采烈的Copy到洛谷上之后
    你就会直接WA了
    因为,题目还是有点不同的(仔细读题)
    区别一:这题不是排列,要离散化
    区别二:这题删掉的不是数字,而是位置

    好了回归正题,讲讲CDQ分治怎么写
    首先,给所有删掉的数编个号,就按照删去的顺序来吧
    没有删掉的数就编个INF吧

    那么,删掉这个数之后,减少的逆序对对数是:
    对于(jin[1,j])
    (t[i]<t[j]),其中t是删除的编号
    并且
    (i<j,a[i]>a[j])
    或者
    (i>j,a[i]<a[j])
    所以,删除的编号直接sort搞完
    剩下的两维CDQ分治

    于是,发现这个玩意是一个三维偏序
    所以之前写过的树状数组套平衡树当然也可以做啦
    但是,CDQ分治还是要会嗷。。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define MAX 50000
    inline int read()
    {
    	int x=0,t=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=-1,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return x*t;
    }
    int n,m,S[MAX],a[MAX],b[MAX],c[MAX],d[MAX];
    long long ans;
    int lowbit(int x){return x&(-x);}
    void Add(int x,int w){while(x<=n)c[x]+=w,x+=lowbit(x);}
    int getsum(int x){int ret=0;while(x)ret+=c[x],x-=lowbit(x);return ret;}
    struct Node
    {
    	int t,p,a;
    	int s;
    }t[MAX];
    bool operator<(Node a,Node b){return a.t<b.t;}
    bool cmp(Node a,Node b){return a.p<b.p;}
    void CDQ(int l,int r)
    {
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	CDQ(l,mid);CDQ(mid+1,r);
    	sort(&t[l],&t[mid+1],cmp);
    	sort(&t[mid+1],&t[r+1],cmp);
    	int j=mid;
    	for(int i=l;i<=mid;++i)
    	{
    		while(j<r&&t[j+1].p<t[i].p)++j,Add(t[j].a,1);
    		t[i].s+=getsum(n)-getsum(t[i].a);
    	}
    	for(int i=mid+1;i<=j;++i)Add(t[i].a,-1);
    	j=r+1;
    	for(int i=mid;i>=l;--i)
    	{
    		while(j>mid+1&&t[j-1].p>t[i].p)--j,Add(t[j].a,1);
    		t[i].s+=getsum(t[i].a-1);
    	}
    	for(int i=r;i>=j;--i)Add(t[i].a,-1);
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;++i)S[i]=a[i]=read();
    	sort(&S[1],&S[n+1]);
    	for(int i=1;i<=n;++i)b[a[i]=lower_bound(&S[1],&S[n+1],a[i])-S]=i;
    	for(int i=n;i;i--)ans+=getsum(a[i]),Add(a[i],1);
    	for(int i=1;i<=n;++i)t[i].t=n+1,t[i].p=i,t[i].a=a[i];
    	for(int i=1;i<=m;++i)
    	{
    		d[i]=read();
    		t[d[i]].t=i;
    	}
    	sort(&t[1],&t[n+1]);
    	memset(c,0,sizeof(c));
    	CDQ(1,n);
    	for(int i=1;i<=n;++i)c[t[i].p]=t[i].s;
    	printf("%lld ",ans);
    	for(int i=1;i<=m;++i)
    		printf("%lld ",ans=ans-c[d[i]]);
    	return 0;
    }
    
    
  • 相关阅读:
    s:set标签
    Oracle创建表语句(Create table)语法详解及示例
    jsp:useBean
    四则运算练习
    Navicat Premium无法连上ORACLE数据库的几种问题解决方法
    Ubuntu开机等待5分钟的取消方法
    Ubuntu内网、外网网络IP地址配置
    eclipse中常用的快捷键汇总
    oracle死锁进程及杀死死锁进程
    oracle导入导出表数据
  • 原文地址:https://www.cnblogs.com/cjyyb/p/8127824.html
Copyright © 2020-2023  润新知