zoukankan      html  css  js  c++  java
  • [Luogu] P1966 火柴排队

    (Link)

    Description

    涵涵有两盒火柴,每盒装有(n)根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:(sum (a_i-b_i)^2)

    其中(a_i)表示第一列火柴中第(i)个火柴的高度,(b_i)表示第二列火柴中第(i)个火柴的高度。

    每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对(10^8-3)取模的结果。

    Solution

    注意到(sum(a_i-b_i)^2=sum{a_i^2}+sum{b_i^2}-2sum{a_ib_i})。而前面两项都是不会改变的,所以只能考虑改变最后一项(2sum{a_ib_i})了。

    因为我们要最小化(sum(a_i-b_i)^2),所以要最大化(sum{a_ib_i})。现在给出一个结论:顺序积之和(ge)无序积之和。这是为什么呢?

    我们考虑一个子问题:设(a_1<a_2,b_1<b_2),那么(a_1b_1+a_2b_2>a_1b_2+a_2b_1)。证明就是直接移项,有(a_2(b_2-b_1)>a_1(b_2-b_1)),又(b_1<b_2),所以(a_2>a_1),即(a_1<a_2),证毕。

    那么对于两个无序的序列(a_i,b_i),我们通过类似冒泡排序的思想,总能通过交换若干项,使总贡献越来越大,最后总能变成两个顺序数列,使贡献达到最大。

    我们把原来的(a_i,b_i)离散化,那么原问题就转化成了另一个问题:对于两个(1sim{n})的排列(a_i,b_i),试通过交换若干项,使得(forall{iin[1,n]},a_i=b_i)

    这个问题也是比较好解决的。我们先把(a_i)转化成顺序数列,然后记录一下(to[a[i]]=i),表示(a_i)这个数变成了(i)这个数。再把(b_i)进行相应转化,即(b_i=to[b_i])。则现在问题又变成另一个更简单的问题:对于一个无序排列,最少要交换几次,使它变成有序?

    这其实就是一个求逆序对啦。因为你要把(1sim{n})(n)个数从小到大依次交换到对应的位置(a_i=i)。通过手动模拟可以发现,数(i)移到(a_i)这个位置需要的交换次数就是(sumlimits_{j=1}^{i-1}|a_j>i|)。那么这就是求逆序对了。具体可以用树状数组实现。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define lowbit(x) ((x) & (-x))
    
    const int mod = 1e8 - 3;
    
    int n, res, a[100005], b[100005], p[100005], q[100005], to[100005], c[100005];
    
    int read()
    {
    	int x = 0, fl = 1; char ch = getchar();
    	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
    	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    	return x * fl;
    }
    
    void add(int x, int d)
    {
    	while (x <= n)
    	{
    		c[x] += d;
    		x += lowbit(x);
    	}
    	return;
    }
    
    int query(int x)
    {
    	int s = 0;
    	while (x)
    	{
    		s += c[x];
    		x -= lowbit(x);
    	}
    	return s;
    }
    
    int main()
    {
    	n = read();
    	for (int i = 1; i <= n; i ++ ) a[i] = read(), p[i] = a[i];
    	for (int i = 1; i <= n; i ++ ) b[i] = read(), q[i] = b[i];
    	sort(p + 1, p + n + 1); sort(q + 1, q + n + 1);
    	for (int i = 1; i <= n; i ++ )
    	{
    		a[i] = lower_bound(p + 1, p + n + 1, a[i]) - p;
    		b[i] = lower_bound(q + 1, q + n + 1, b[i]) - q;
    	}  
    	for (int i = 1; i <= n; i ++ )
    		to[a[i]] = i;
    	for (int i = 1; i <= n; i ++ )
    		b[i] = to[b[i]];
    	for (int i = 1; i <= n; i ++ )
    	{
    		add(b[i], 1);
    		res = (res + query(n) - query(b[i])) % mod;
    	}
    	printf("%d
    ", res);
    	return 0;
    }
    
  • 相关阅读:
    网页的尺寸
    前端大杂烩总结
    vscode 2.0 配置
    关于node 版本的一个奇葩的问题 :HPE_UNEXPECTED_CONTENT_LENGTH error #3103
    ADO.NET 增删改查的基本用法
    面向对象 类库 委托
    三大特性:封装,继承,多态
    面向对象 抽象类 多态
    SQL数据库基础(九)
    SQL数据库基础(八)
  • 原文地址:https://www.cnblogs.com/andysj/p/13918034.html
Copyright © 2011-2022 走看看