• P1966 【NOIP2013】 火柴排队


    #$Description$ [题面](https://www.luogu.org/problem/P1966) 有两列火柴,每个火柴有一个高度,定义两列火柴之间的距离为$sumlimits_{i=1}^n (a_i-b_i)^2$,其中$a_i$表示第一列火柴中第$i$个火柴的高度,$b_i$表示第二列火柴中第$i$个火柴的高度 可以交换一列中任意两根相邻火柴的位置,使得最终两列火柴距离最小,求最少交换次数 #$Solution$ 想让两列火柴距离最小,显然要使第一列中第$i$高的火柴对应第二列中第$i$高的火柴,证明略。

    分别开两个数组(c,d)(c[i])表示在(a)数组中第(i)大的数所在的位置(如果出现相同的数按照下标排序)

    比如下面这个例子,理解一下

    c 1 4 5 2 3
    a 1 3 5 1 2

    (d[i])同理表示(b)数组

    这实际是一种离散化,不过我们常用的离散化(c[i])表示(i)的排名,这个离散化(c[i])表示排名为(i)所在的下标

    然后令(p[c[i]]=d[i]),表示他们之间的对应关系

    比如下面这样

    c 1 4 5 2 3
    d 3 2 1 5 4
    p 3 5 4 2 1

    最终要求在各自数组中排名相同的在同一位置,即(p[i]=i)

    所以要把(p)数组变成一个升序数列,假如(p[5]=3,p[3]=5)表示表示排名为(i)的元素在(a)中是第(5)个,在(b)中是第(3)个,排名为(j)的元素在(a)中是第(3)个,在(b)中是第(5)

    结论:使(p)数组由无序变为升序(每次只能交换相邻两个数),最小操作个数是逆序对数量之和。

    因此对(p)数组求逆序对之和就行

    (Code)

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define re register
    #define maxn 101010
    #define ll long long
    #define mod  99999997
    #define ls p<<1
    #define rs p<<1|1
    using namespace std;
    inline int read()
    {
    	int x=0,f=1; char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    ll ans;
    int len1,len2;
    ll sums[maxn<<2];
    int a[maxn],b[maxn],p[maxn],n;
    struct PCC{
    	int id,num;
    }c[maxn],d[maxn];
    bool cmp(PCC A,PCC B)
    {
    	if(A.num!=B.num)
    	return A.num<B.num;
    	
    	return A.id<B.id;
    }
    void DCZ1()
    {
    	sort(c+1,c+n+1,cmp);
    	for(re int i=1;i<=n;++i)
    	 a[i]=c[i].id;	
    } 
    void DCZ2()
    {
    	sort(d+1,d+n+1,cmp);
        for(re int i=1;i<=n;++i)
         b[i]=d[i].id;
    }
    void push_up(int p)
    {
    	sums[p]=sums[ls]+sums[rs];
    }
    void insert(int p,int l,int r,int k)
    {
    	if(l==r)
    	{
    		sums[p]++;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(k<=mid) insert(ls,l,mid,k);
    	else insert(rs,mid+1,r,k);
    	push_up(p);
    }
    int query(int p,int l,int r,int k)
    {
    	int tmp=0;
    	if(l==r)
    	{
    		if(l<=k) tmp+=sums[p];
    		return tmp;
    	}
    	int mid=(l+r)>>1;
    	if(k>mid) tmp+=sums[ls],tmp+=query(rs,mid+1,r,k);
    	else tmp+=query(ls,l,mid,k);
    	return tmp; 
    }
    void solve()
    {
    	for(re int i=1;i<=n;++i)
    	{
    		ll tmp=(i-1)-query(1,1,n,p[i]);
    		tmp=(tmp%mod+mod)%mod;
    		ans=(ans+tmp)%mod;
    		insert(1,1,n,p[i]);
    	}
    }
    int main()
    {
    	n=read();
    	for(re int i=1;i<=n;++i) a[i]=read(),c[i].num=a[i],c[i].id=i;
    	for(re int i=1;i<=n;++i) b[i]=read(),d[i].num=b[i],d[i].id=i;
    	DCZ1(),DCZ2();
    	for(re int i=1;i<=n;++i)
    	p[a[i]]=b[i];
    	solve();
    	printf("%lld
    ",ans);	 	
    	return 0;
    }
    
  • 相关阅读:
    JS/JQuery获取当前元素的上一个/下一个兄弟级元素等元素的方法
    目前流行前端几大UI框架
    C#客户端嵌入Chrome浏览器的实现
    ARM编译中的RO、RW和ZI DATA区段
    如何成为优秀的技术Leader?做到这三点就够了
    VMware虚拟机中的CentOS安装Nginx后本机无法访问的解决办法
    Centos下安装nginx步骤解析
    centos修改、保存文件的详细步骤
    configure: error: C preprocessor "arm-linux-gnueabihf-g++" fails sanity check
    有关fgetc配合feof逐行读取文件最后一行读取两遍的错觉?
  • 原文地址:https://www.cnblogs.com/Liuz8848/p/11742015.html
Copyright © 2020-2023  润新知