zoukankan      html  css  js  c++  java
  • 洛谷P1966 火柴排队——题解

    P1966 火柴排队

    题解:

      将总距离表达式展开,尝试化简。发现平方的和是不变的,变的只有乘积,使Σaibi最大即可保证距离最小,自然想到最大的a让最大的b乘的贪心想法,即最终的顺序是a和b都从小到大(或从大到小)一一对应。尝试证明。

      对于顺序的贪心证明,可从一步步调整入手。即假设存在ax>ay,bx>by,若x与x相对,答案为axbx+ayby;若x与y相对,则答案为axby+aybx,作差提取公因式可发现前者大于后者,故知答案序列必为顺序对应。

      工作没有结束,题目并不要求最小的距离值,而是最小相邻交换次数。发现两个序列的交换是独立的(寻找性质),即可以先完成第一个序列的所有交换,再完成第二个序列的所有交换。找到这个性质后可以很大程度上简化思考(不然容易想破头皮也得不出结论)。对于同时处理两个序列的交换,我们还是更倾向于处理一个序列的交换。故尝试证明两个序列的交换可以等价到一个序列的交换。记两个序列分别为A、B序列,在做完A序列的交换后,发现接下来要在B序列做的所有交换按顺序对A序列做的话,最终A、B序列的对应关系是一样的,故我们只处理一个序列交换即可。

      由于序列可以离散化,且离散化后两序列值域一样,不妨做个离散化,将序列中数的对应关系简化为相等关系,那么现在问题变为求一个序列A排成另一个序列B的最小交换次数。再做个简化,我们给B序列上的数从第一个位置开始按顺序标上编号,再把A序列的值改为这个值在B序列上的编号,则问题又转化为求一个序列A从小到大排成顺序的最小交换次数。

      接下来一段时间可能想不出什么新东西了,不妨分析一下题目提到的操作:交换相邻两个数。思考这个操作在与题目有关的知识点上的意义:在排序里,交换相邻两个数可以消灭且只消灭一个相邻异序对。于是我们又找到了新的入手点。最后的顺序的异序对的个数为0,而一个操作最多消灭一个相邻异序对。异序对一定会相邻吗?思考一下,发现若存在异序对,则必会存在一个相邻异序对(考虑一对异序对a,b,设a,b之间没有相邻异序对,则a,b之间的数必从小到大排好了序,发现这个有序区间的端点必定有一个会与a或b组成一个相邻异序对),可发现最少操作次数即等于异序对个数。可用归并排序nlogn时间求出。

      时间复杂度:O(n log n)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 
     5 using namespace std;
     6 
     7 const int N=1e5+5;
     8 
     9 struct node{
    10     int num,ord,li;
    11 }a[N],b[N];
    12 
    13 inline int read()
    14 {
    15     int x=0;
    16     char ch=getchar();
    17     while(!isdigit(ch))
    18         ch=getchar();
    19     while(isdigit(ch))
    20         x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    21     return x;
    22 }
    23 
    24 int n,xord[N],xu[N],ins[N];
    25 
    26 long long mod=1e8-3,ans;
    27 
    28 inline bool cmp1(const node &a,const node &b)
    29 {
    30     return a.num<b.num;
    31 }
    32 
    33 inline bool cmp2(const node &a,const node &b)
    34 {
    35     return a.ord<b.ord;
    36 }
    37 
    38 long long rev(int l,int r)
    39 {
    40     if(l==r)
    41         return 0;
    42     if(l+1==r)
    43     {
    44         if(xu[l]>xu[r])
    45         {
    46             swap(xu[l],xu[r]);
    47             return 1;
    48         }
    49         return 0;
    50     }
    51     int mid=(l+r)>>1;
    52     long long ret=rev(l,mid)+rev(mid+1,r);
    53     for(int i=l;i<=r;++i)
    54         ins[i]=xu[i];
    55     int ll=l,rr=mid+1,cnt=l;
    56     while(ll<=mid&&rr<=r&&cnt<=r)
    57     {
    58         if(ins[ll]<ins[rr])
    59             xu[cnt++]=ins[ll++];
    60         else
    61         {
    62             xu[cnt++]=ins[rr++];
    63             ret+=mid-ll+1;
    64         }
    65     }
    66     if(cnt<=r)
    67     {
    68         while(ll<=mid)
    69             xu[cnt++]=ins[ll++];
    70         while(rr<=r)
    71             xu[cnt++]=ins[rr++];
    72     }
    73     return ret%mod;
    74 }
    75 
    76 int main()
    77 {
    78     n=read();
    79     for(int i=1;i<=n;++i)
    80         a[i].num=read(),a[i].ord=i;
    81     for(int i=1;i<=n;++i)
    82         b[i].num=read(),b[i].ord=i;
    83     sort(a+1,a+n+1,cmp1);
    84     sort(b+1,b+n+1,cmp1);
    85     for(int i=1;i<=n;++i)
    86         a[i].li=b[i].li=i;
    87     sort(a+1,a+n+1,cmp2);
    88     sort(b+1,b+n+1,cmp2);
    89     for(int i=1;i<=n;++i)
    90         xord[a[i].li]=i;
    91     for(int i=1;i<=n;++i)
    92         xu[i]=xord[b[i].li];
    93     printf("%lld",rev(1,n));
    94     return 0;
    95 }

    什么?你不会归并排序求逆序对?戳:逆序对小记

    总之,这道题目在很多方面用了非常多简化虽然很毒瘤,但一步步化到最后的感觉真的很奇妙

  • 相关阅读:
    Wannafly 挑战赛12 E
    HIT ACM 2018春 week2 codeforces.com/gym/101652 题解
    Hihocoder [Offer收割]编程练习赛49 题目4 : 第K小先序遍历
    HDU
    ZOJ
    HYSBZ
    POJ
    HYSBZ
    POJ 2796 Feel Good 题解
    逆元基本知识
  • 原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/13620787.html
Copyright © 2011-2022 走看看