zoukankan      html  css  js  c++  java
  • LG1393 动态逆序对

    问题描述

    LG1393


    题解

    本题可以使用(mathrm{CDQ})分治完成。


    二维偏序

    根据偏序的定义,逆序对是一个二维偏序,但这个二维偏序比较特殊:

    • (i>j,a_i<a_j)

    • (i<j,a_i>a_j)

    以上两种情况都符合这个二维偏序。


    三维偏序

    但带修改二维偏序怎么做?

    我们将删除操作视为插入操作。

    则没有没删除的插入时间为(1),第(i)个被删除的,插入时间为(m-i+2)

    将插入时间作为第一维,数列位置为第二维,数值为第三维。

    做三维偏序即可。

    注意由于逆序对的定义,三维偏序仍是两种情况。


    (mathrm{CDQ})分治

    简要介绍一下(mathrm{CDQ})分治。

    (mathrm{CDQ})分治是一种分治算法(废话),是一个叫做陈丹琪的女选手在(2009)(记不清了)年提出的。当时提出时是用于优化一类(mathrm{DP})问题的。后来(mathrm{CDQ})分治被主要用于解决三维偏序问题。

    (mathrm{CDQ})分治思想类似于归并排序。归并排序每次将区间([l,r])划分为([l, lfloorfrac{l+r}{2} floor])([lfloor frac{l+r}{2} floor + 1,r])

    考虑三维偏序问题模型,(i)(j)有贡献需要满足(a_i<a_j,b_i<b_j,c_i<c_j)

    (mathrm{CDQ})分治先将(a)升序排序,再进行归并排序,使(b)有序。

    考虑区间([l,r]),当完成左右区间的归并排序后,由于一开始先对(a)进行了排序,则(forall x in[l, lfloorfrac{l+r}{2} floor],forall y in [lfloor frac{l+r}{2} floor + 1,r]),一定有(a_x<a_y),这时候分别维护左区间和右区间指针,同时用树状数组维护值即可。

    具体请见【模板】三维偏序(陌上花开)题解区。


    (mathrm{code})

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,m,tmp;
    struct node{
    	int a,b,c,ans;
    }a[40007];
    int anss[40007];
    template<typename Tp>
    void read(Tp &x){
    	x=0;char ch=1;int fh;
    	while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();
    	if(ch=='-'){
    		ch=getchar();fh=-1;
    	}
    	else fh=1;
    	while(ch>='0'&&ch<='9'){
    		x=(x<<1)+(x<<3)+ch-'0';
    		ch=getchar();
    	}
    	x*=fh;
    }
    
    struct BIT{
    	int c[40007];
    	void change(int p,int k){
    		while(p<=40000){
    			c[p]+=k;p+=(p&(-p));
    		}
    	}
    	int query(int p){
    		int re=0;
    		while(p){
    			re+=c[p];p-=(p&(-p));
    		}
    		return re;
    	}
    }T;
    
    bool comp(node a,node b){
    	if(a.a!=b.a) return a.a<b.a;
    	if(a.b!=b.b) return a.b<b.b;
    	return a.c<b.c;
    }
    
    bool cmp(node a,node b){
    	if(a.b!=b.b) return a.b<b.b;
    	return a.c<b.c;
    }
    
    bool fuck(node a,node b){
    	if(a.b!=b.b) return a.b>b.b;
    	return a.c>b.c;
    }
    
    void cdq(int l,int r){
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	cdq(l,mid);cdq(mid+1,r);
    	sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
    	int i,j=l,tot=0;
    	for(i=mid+1;i<=r;i++){
    		while(j<=mid&&a[j].b<=a[i].b){
    			T.change(a[j].c,1);++j;++tot;
    		}
    		a[i].ans+=tot-T.query(a[i].c);
    	}
    	for(i=l;i<j;i++) T.change(a[i].c,-1);
    	sort(a+l,a+mid+1,fuck);sort(a+mid+1,a+r+1,fuck);
    	for(i=mid+1,j=l;i<=r;i++){
    		while(j<=mid&&a[j].b>=a[i].b){
    			T.change(a[j].c,1);++j;
    		}
    		a[i].ans+=T.query(a[i].c);
    	}
    	for(i=l;i<j;i++) T.change(a[i].c,-1);
    }
    
    bool fake(node a,node b){
    	return a.c<b.c;
    }
    
    void lsh(){
    	sort(a+1,a+n+1,fake);
    	for(register int i=1;i<=n;i++) a[i].c=i;
    }
    
    signed main(){
    	read(n);read(m);
    	for(register int i=1;i<=n;i++){
    		read(a[i].c);a[i].a=1,a[i].b=i;
    	}
    	for(register int i=1;i<=m;i++){
    		read(tmp);a[tmp].a=m-i+2;
    	}
    	lsh();
    	sort(a+1,a+n+1,comp);
    	cdq(1,n);
    	for(register int i=1;i<=n;i++) anss[a[i].a]+=a[i].ans;
    	for(int i=1;i<=n;i++)anss[i]+=anss[i-1];
    	for(int i=m+1;i>0;i--)printf("%lld%c",anss[i]," 
    "[i==1]);
    	return 0;
    }
    
  • 相关阅读:
    围观窗体与组件01 零基础入门学习Delphi23
    浅谈Delphi过程与函数03 零基础入门学习Delphi22
    围观窗体与组件01 零基础入门学习Delphi23
    使用MASM09 Win32汇编语言017
    使用MASM10(变量的使用) Win32汇编语言018
    使用MASM08 Win32汇编语言016
    使用MASM08 Win32汇编语言016
    浅谈Delphi过程与函数03 零基础入门学习Delphi22
    游戏服务器设计探讨
    简单网络编程TCP客户端示例
  • 原文地址:https://www.cnblogs.com/liubainian/p/11503572.html
Copyright © 2011-2022 走看看