[AGC052E] 3 Letters 题解

题目链接

点击打开链接

题目解法

这题真是太牛了(我直接报做法了,我不知道这个是如何想到的

\(A,B,C\) 分别当成 \(0,1,2\),把这样转化后的序列记为 \(s\)
我们构造出序列 \(v\),满足 \(\forall 2\le i\le n,\;|v_i-v_{i-1}|=1\)\(v_i\equiv s_i(\bmod 3)\)
不难发现,如果固定了 \(v_1\) 和序列 \(s\),序列 \(v\) 是唯一的

一次操作对应的是 \(v_i+=2\)\(v_i-=2\),且需要满足上面的条件
我们先对于初始和结束序列,求出任意一个合法的 \(v\),记为 \(vA,vB\)
我们通过使 \(vA\) 中的每个元素 \(+3\) 调整,使得 \(vA_i\equiv vB_i(\bmod 2)\)

现在,我们证明操作次数可以取到下界 \(\sum\frac{|vA_i-vB_i|}{2}\)
即我们需要证明:在 \(vA\neq vB\) 的任意时刻,都可以操作,使得上面的值变小
我们取出满足 \(vA_i>vB_i\) 的最大的 \(vA_i\),将其 \(-2\)
为什么这个操作一定是合法的?
如果不合法,不妨令 \(a_{i-1}=a_i+1\),且 \(b_{i-1}\ge a_{i-1}\)
这说明 \(b_{i-1}\ge a_{i-1}>a_i>b_i\),则 \(|b_{i-1}-b_i|>1\),矛盾
所以一定可以操作

\(delta_i=|vA_i-vB_i|\),我们现在需要找一个 \(6\) 的倍数 \(x\),使得 \(\sum\limits_{i=1}^n|delta_i-x|\) 最小
直接在 \(delta\) 的中位数两边求,取个 \(min\) 就是答案
时间复杂度 \(O(n\log n)\),用桶排可以做到 \(O(n)\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
    FF=0;int RR=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    FF*=RR;
}
const int N=500010;
int n,A[N],B[N],vA[N],vB[N],det[N];
char str[N];
int main(){
    read(n);
    scanf("%s",str+1);
    F(i,1,n) A[i]=str[i]-'A';
    scanf("%s",str+1);
    F(i,1,n) B[i]=str[i]-'A';
    vA[1]=A[1];
    F(i,2,n){
        if((A[i-1]+1)%3==A[i]) vA[i]=vA[i-1]+1;
        else vA[i]=vA[i-1]-1;
    }
    vB[1]=B[1];
    F(i,2,n){
        if((B[i-1]+1)%3==B[i]) vB[i]=vB[i-1]+1;
        else vB[i]=vB[i-1]-1;
    }
    if((vA[1]-vB[1])&1) F(i,1,n) vA[i]+=3;
    F(i,1,n) det[i]=vA[i]-vB[i];
    sort(det+1,det+n+1);
    int mid=det[(n+1)/2];
    while(mid%6) mid++;
    LL val1=0,val2=0;
    F(i,1,n) val1+=abs(det[i]-mid),val2+=abs(det[i]-mid+6);
    printf("%lld\n",min(val1,val2)/2);
    return 0;
}

posted @ 2024-08-26 15:14  Farmer_D  阅读(33)  评论(0)    收藏  举报