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;
    }
    
  • 相关阅读:
    经典网页设计:漂亮的个人作品集网站设计欣赏【中篇】
    引领网页设计潮流的优秀网页作品赏析《第二季》
    Chance – 功能强大的 JavaScript 随机数生成类库
    设计前沿:16款扁平风格 iOS 7 图标设计
    TwentyTwenty – 使用 jQuery 实现图片对比功能
    未来的 Web:九个不可思议的 WebGL 应用试验
    推荐25个帮助你提高技能的 CSS3 实战教程
    经典网页设计:顶尖的个人作品集网站设计欣赏【上篇】
    Smint – 用于单页网站制作的 jQuery 导航菜单插件
    关注经典:CSS Awards 获奖网站作品赏析《第一季》
  • 原文地址:https://www.cnblogs.com/andysj/p/13918034.html
Copyright © 2011-2022 走看看