ATG32E Modulo Pairing 笔记

原题链接:洛谷传送门

题意简述

给定 2×N 个整数 ai,满足 0ai<M。需要把这些整数分成 N 对,每一对 (x,y) 的权值为 (x+y)modM。我们定义,一种分配方案的权值为所有整数对权值中的最大值。求分配方案权值的最小可能值。

N105,M109

解决方案

不妨先考虑没有取模操作时的情况。显然我们先排个序,让最大的和最小的一对,次大的和次小的一对,依此类推,这样如括号嵌套的方案是最优的。也即 a1a2a3a4(a1,a4) 一组,(a2,a3) 一组最优。

(a1,a4) 权值为 p(a2,a3) 权值记为 q

首先,(a1,a2) 一组,(a3,a4) 一组显然不优。
接下来,(a1,a3) 一组(其权值记为 p),(a2,a4) 一组(其权值记为 q)显然也不优:

  1. pq,则 q=p+(a2a1)p,不优。
  2. pq,则 q=q+(a4a3)q,不优。

综上证毕。

啊但是现在加入了取模操作,怎么办呢?

首先我们发现,仍然有 max((a1+a4M),(a2+a3M))max((a1+a3M),(a2+a4M))max((a1+a2M),(a3+a4M))

不等式两边同增同减。显然。

这说明对于最终被取模的数对中的数,最优的方案也是如括号嵌套的。

接下来我们推导出:max((a3+a4M),(a1+a2))max((a2+a4M),(a1+a3))max((a1+a4M),(a2+a3))

显然有 aiM<0
a1+a2a1+a3,a3+(a4M)<a3+a1
max((a3+a4M),(a1+a2))max((a2+a4M),(a1+a3))
a2+(a4M)a2+a3,a1+a3a2+a3
max((a2+a4M),(a1+a3))max((a1+a4M),(a2+a3))

又推导出 max((a3+a4M),(a1+a2))max((a2+a3M),(a1+a4))

a1+a2a1+a4,a3+(a4M)<a3+a1
max((a3+a4M),(a1+a2))max((a2+a3M),(a1+a4))

这告诉我们,需要取模的数对一定完全在不需要取模的对后面。因此,整体的匹配形成了这样一种形状:

pECgGqg.png

也就是说,以某个下标 k 为界,[1,k) 的元素间互相拉数对,[k,2×N] 的元素间互相拉数对。

不过,这两部分的分界线怎么确定呢?实际上,这个分界线越靠左越好;也就是说我们要尽可能地从不减 M 的那些数对中匀出能减去 M 的一对对数。为什么呢?

半感性半理性地理解一下。左边的答案肯定不会更劣,因为删除一些元素不可能让这个集合通过加法规则组合出来的最大值变大;右边的答案也不会更劣,具体来说见下文:

显然我们肯定是尝试将数对里更大的元素拎出去,因为如果把更小的元素拎出去都行,那拎大的肯定更行且更有利于最小化答案。

这样,设我们调整之前的数对为 (a1,a2)(b1,b2),且 a1a2,b1b2。那么调整之后的数对就是 (a1,b1)(a2,b2)

现在我们需要证明 max((a1+b1),(a2+b2M))max((a1+a2),(b1+b2))

显然 a2+b2M<a2<a1+a2
a1+b1>a1+a2 当且仅当 b1>a2。此时 a1+b1a2+b1<b1+b2
由此得证。

现在考虑代码实现。首先 O(NlogN) 做法显然,二分就行了;但是我们有更优美的 O(N) 做法。考虑这样一种单调性,对于元素 ai 来说总是存在一个下标 xi 使得 ai+a[xi,2×N]M,ai+a[1,xi),且对于 i<jxixj。于是我们就可以用双指针扫一遍,求解所有的 xi。而对于 ai 来说 ki=i(Nxi) 就是保证它合法的最左分界线。最终确定的分界线即为 maxki(注意,如果此结果是个偶数还要加一)。排序可以用基数排序做到 O(N)

代码

除排序之外 O(N)

#include <bits/stdc++.h>
using namespace std;
const int MaxN=1e5+5;
int M,N,A[MaxN<<1];
void maxxer(int &x,int y){x=max(x,y);}
int main(){
    scanf("%d%d",&N,&M);
    for(int i = 1;i <= N*2;i++)scanf("%d",&A[i]);
    sort(A+1,A+N*2+1);A[N*2+1]=M;int bkp=0;
    for(int i=1,j=N*2+1;i<=N*2;i++){
        while(A[j-1]+A[i]>=M)j--;
        maxxer(bkp,i-(N*2-j));
    }
    if(bkp%2==0)bkp++;int ans=0;
    for(int i=1,j=bkp-1;i<j;i++,j--)maxxer(ans,(A[i]+A[j])%M);
    for(int i=bkp,j=N*2;i<j;i++,j--)maxxer(ans,(A[i]+A[j])%M);
    printf("%d",ans);
    return 0;
}

反思与总结

思考过程上,我们先从平常没有取模的情况开始思考起,然后推广到多出一个限制的情况,这是一种由平常到特别的思想。除此之外,本题关于序和最优性的大量结论还需要一定的推导能力。最后代码实现注意一些细节就行。

posted @   矞龙OrinLoong  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示