题解:AT_abc467_e [ABC467E] Adjacent Sums (hard)
分享一种神秘且麻烦的做法。
首先随意地构造一组 \(c_1,c_2,\dots ,c_n\),使得 \(c_i+c_{i+1}=b_i\)。一种简单的构造方法是令 \(c_1=0\),后面的多退少补。
然后因为 \(a_i+a_{i+1} \equiv b_i \Leftrightarrow a_i+a_{i+1}-b_i \equiv 0 \Leftrightarrow (a_i-c_i)+(a_{i+1}-c_{i+1}) \equiv 0\),所以题目条件就可以看成给 \(a_i+c_i\) 加上总和最小数,使得相邻两项的和能被 \(M\) 整除。下文所述的 \(a_i\) 即为 \(a_i+c_i\)。
发现最终的序列取模后一定是形如 \(x,m-x,x,m-x,x,\dots\) 的,所以如果钦定 \(a_1\) 最后的值(设其为 \(v\)),就能把整个序列还原。
现在考虑如何快速对于一个 \(v\) 算出答案。先把奇数项和偶数项分开,则奇数项都要变成 \(v\),偶数项都要变成 \(m-v\)。只保留奇数项,那么小于等于 \(v\) 的 \(a_i\) 需要变成 \(v\),大于 \(v\) 的 \(a_i\) 需要变成 \(m+v\)。将 \(a\) 排序后找到最后一个小于等于 \(v\) 的项 \(a_k\),再设奇数项共有 \(h\) 个,那么总代价就是 \(h\times v+(h-k)\times m-\sum a_i\)。二分找到 \(k\),然后直接计算即可。偶数项同理。
但是 \(v\) 的可能取值是 \(10^9\) 级别的,无法一个个枚举。通过上面的式子容易发现一个能取到最小值的 \(v\) 必然等于 \(0\),或某个 \(a_i\)(奇数项)或 \(m-a_i\)(偶数项),因为如果 \(v\) 在两个上述值之间,将其减一后含 \(m\) 的项不会变(\(k\) 不变),而含 \(v\) 的项不变(奇偶的 \(h\) 相等)或变小 (奇项的 \(h\) 多一)。
最终的做法就是拿 \(v\) 的每个可能取值试一试,求最小值。需要注意的是 \(v=m\) 的答案需要特判,因为此时 \(a_i=0\) 的项不用变化。然后要开 long long。
代码实现的时候把二分换成了双指针。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
#define int long long
int n,m,a[N],b[N*2],f[N*2];
vector<int> c;
void solve1()
{
for(int i=1;i<=n;i+=2)
c.push_back(a[i]);
sort(c.begin(),c.end());
auto it=c.begin();
for(int i=1;i<=n;i++)
{
while(it!=c.end()&&*it<=b[i])
it++;//双指针找到 k
f[i]=1ll*(c.end()-it)*m+1ll*(n+1)/2*b[i];
}
}
void solve2()
{
c.clear();
for(int i=2;i<=n;i+=2)
c.push_back(a[i]);
sort(c.begin(),c.end());
auto it=c.begin();
for(int i=n;i>=1;i--)
{
b[i]=m-b[i];//偶数项的 v 实际是 m-v
while(it!=c.end()&&*it<=b[i])
it++;
f[i]+=1ll*(c.end()-it)*m+1ll*n/2*b[i];
}
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=2;i<=n;i++)
{
scanf("%lld",&b[i]);
(b[i]-=b[i-1])%=m;
(a[i]+=m-b[i])%=m;//将 a[i] 变成 a[i]-c[i]
}
for(int i=1;i<=n;i+=2)
b[i]=a[i];
for(int i=2;i<=n;i+=2)
b[i]=m-a[i];
sort(b+1,b+n+1);
solve1();//计算奇数项
solve2();//计算偶数项
int sum=0,ans=0;
for(int i=1;i<=n;i++)
{
sum+=a[i];
if(a[i])
ans+=m-a[i];
}//特判 v==n
for(int i=1;i<=n;i++)
ans=min(ans,f[i]-sum);
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号