题解 CF1214F 【Employment】

ljc20020730

2019-09-11 21:09:25

Solution

首先,最优的方案必然满足: > 按照$a,b$升序排列,指定$a_1$对应$b_k$,那么$a_i$必然对应$b_{(k+i-1)\% n + 1}$ 换句话说,只要确定一个断环的位置,那么最优的方案就唯一确定了,在断环后的序列中,每一个人都去寻找自己最近的一个目标。 可以用微扰法简单证明。 > 若存在两个相邻元素$x,y$,设其目标为$b_x,b_y$,但是$p_{b_x} > p_{b_y}$,那么显然交换两个元素的目标,答案会更优。 通过这个贪心,问题就转化为: > 对$a,b$排序后,设$d\in[0,n)$为增量 , 求$\min\limits_{d=0}^{n-1} \{\sum\limits_{i=1}^{n} dist(a_i,b_{i+d})\}$ 于是我们可以用$O(n^2)$来求解。 为了更快速的解决问题,我们需要对上述算法优化。 假设 $j = i + d$,那么区间长度为$d = j - i$。 - $a_i \leq b_j$ , $b_j - a_i \leq a_i - b_j + m$ , 贡献为$(-a_i) + (b_j)$ - $a_i \leq b_j$ , $b_j - a_i > a_i - b_j + m$ , 贡献为$(-a_i+ m) + (b_j)$ - $a_i > b_j$ , $a_i - b_j \leq b_j - a_i + m$ , 贡献为$(a_i) - (b_j)$ - $a_i > b_j$ , $a_i - b_j > b_j - a_i + m$ , 贡献为$(-a_i) + (b_j +m)$ 对于每一个增量$d$的答案是$f_d$,那么显然这些$f_d$都是分别由$a_i$和$b_i$来贡献的。 我们可以使用$O(n)$的复杂度确定一个$a_i$,然后将符合条件的$b_j$来归成四种可能中的一种,分别用$a_i$对应的贡献,去更新$f_{j-i}$的值。 具体来说,若指定的$a_i$让$b_j$在区间$j \in [l,r]$中满足: - $2a_i \leq 2b_j \leq 2a_i+m$ , 我们就用$-a_i$的贡献去更新$d \in [l-i,r-i]$ - $2a_i + m+ 1\leq 2b_j $ , 我们就用$a_i + m$的贡献去更新$d \in [l-i,r-i]$ - $2a_i - m\leq 2b_j\leq 2a_i - 1 $ , 我们就用$a_i$的贡献去更新$d \in [l-i,r-i]$ - $ 2b_j\leq 2a_i - m-1 $ , 我们就用$-a_i$的贡献去更新$d \in [l-i,r-i]$ 注意到,上述的分类讨论我们发现对于一个确定的$a_i$,对于$b_j$数组来说,被切分为最多$4$段,对于每一段里的$d = j - i $都使用相同的关于$a_i$的贡献来区间更新。 这显然可以使用差分来维护。 同理,如果我们使用$O(n)$的复杂度去确定一个$b_j$,那么对应位$a_i$同样会被切分成最多$4$段,每段都使用相同的关于$b_j$的贡献来更新。 本题总复杂度$O(n \ log_2 \ n )$。 ```cpp # pragma GCC optimize(3) # include<bits/stdc++.h> # define int long long using namespace std; const int N=2e5+10; int a[N],b[N],aa[N],bb[N],c[N],ans[N]; int n,m; pair<int,int>t1[N],t2[N]; inline int read() { int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X; } void update(int l,int r,int d){c[l]+=d; c[r+1]-=d;} void modify(int l,int r,int d) { if (l>r) return; l=((l+n)%n+n)%n; r=((r+n)%n+n)%n; if (l>r) update(l,n-1,d),update(0,r,d); else update(l,r,d); } signed main() { m=read();n=read(); for (int i=1;i<=n;i++) t1[i]=make_pair(read(),i); for (int i=1;i<=n;i++) t2[i]=make_pair(read(),i); sort(t1+1,t1+1+n); sort(t2+1,t2+1+n); for (int i=1;i<=n;i++) { a[i]=t1[i].first; aa[i]=a[i]<<1; b[i]=t2[i].first; bb[i]=b[i]<<1; } for (int i=1;i<=n;i++) { int l=1,r=upper_bound(bb+1,bb+1+n,aa[i]-m-1)-bb-1; modify(l-i,r-i,-a[i]); l=r+1; r=upper_bound(bb+1,bb+1+n,aa[i]-1)-bb-1; modify(l-i,r-i,a[i]); l=r+1; r=upper_bound(bb+1,bb+1+n,aa[i]+m)-bb-1; modify(l-i,r-i,-a[i]); l=r+1; r=n; modify(l-i,r-i,a[i]+m); } for (int i=1;i<=n;i++) { int l=1,r=upper_bound(aa+1,aa+1+n,bb[i]-m-1)-aa-1; modify(i-r,i-l,-b[i]); l=r+1; r=upper_bound(aa+1,aa+1+n,bb[i])-aa-1; modify(i-r,i-l,b[i]); l=r+1; r=upper_bound(aa+1,aa+1+n,bb[i]+m)-aa-1; modify(i-r,i-l,-b[i]); l=r+1; r=n; modify(i-r,i-l,b[i]+m); } int pos=0; for (int i=1;i<n;i++) c[i]+=c[i-1],pos=(c[i]<c[pos])?i:pos; printf("%lld\n",c[pos]); for (int i=1,j=pos+1;i<=n;i++,j=(j==n)?1:j+1) ans[t1[i].second]=t2[j].second; for (int i=1;i<=n;i++) printf("%lld ",ans[i]); puts(""); return 0; } ```