P8521 [IOI2021] 修改 DNA
题目链接:P8521 [IOI2021] 修改 DNA。
说句闲话,我最开始做这题的时候这题是 P10050 来着?
upd 24/07/09:修改了一处小错误。
科普一下,生物体中常见的碱基有 A)、鸟嘌呤(G)、胞嘧啶(C)、胸腺嘧啶(T)和尿嘧啶(U),其中碱基 A、G、C 和 T 存在于 DNA 中。注意,本题中 DNA 序列只含有字符 A、T 和 C。
本题虽然是交互题,但实际上是一道传统题。
为了方便,我们把 A、T 和 C 分别替换为
int change(char c){
if(c=='A') return 0;
else if(c=='T') return 1;
else return 2;
}
注意到本题求修改次数的是同一个区间,那么对于任意
那么我们可以在 init 函数中把修改矩阵求出来。这里使用类似前缀和的思路存储。
void init(string a,string b) {
int len=a.length();
For(k,1,len){
F(i) F(j) S[k][i][j]=S[k-1][i][j];
S[k][change(a[k-1])][change(b[k-1])]++; // 类似前缀和
}
}
我们采取这样的思路修改:能两两交换就交换(如样例二),剩下多出来的考虑三个轮换修改。
对于每一组询问,我们先求出这个区间的修改矩阵。然后进行两两交换。此时
int get_distance(int x,int y){
int ans=0;
F(i) F(j) s[i][j]=S[y+1][i][j]-S[x][i][j]; // 求出修改矩阵
F(i){
For(j,i+1,3){
int m=min(s[i][j],s[j][i]);
ans+=m;s[i][j]-=m;s[j][i]-=m; // 抵消
}
}
// 轮换修改
if(s[0][1]!=s[1][2] || s[1][2]!=s[2][0] || s[1][0]!=s[2][1] || s[2][1]!=s[0][2]) return -1;
if(s[0][1]!=0) return ans+2*s[0][1];
else return ans+2*s[1][0];
}
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define For(i,a,b) for(int i=a; i<=b; i++)
#define F(i) For(i,0,2)
int change(char c){if(c=='A'){return 0;}else if(c=='T'){return 1;}else{return 2;}}
int S[100002][3][3];//ATC
int s[3][3];
void init(string a,string b) {
int len=a.length();
For(k,1,len){
F(i) F(j) S[k][i][j]=S[k-1][i][j];
S[k][change(a[k-1])][change(b[k-1])]++; // 前缀和
}
}
int get_distance(int x,int y){
int ans=0;
F(i) F(j) s[i][j]=S[y+1][i][j]-S[x][i][j]; // 求出修改矩阵
F(i){
For(j,i+1,3){
int m=min(s[i][j],s[j][i]);
ans+=m;s[i][j]-=m;s[j][i]-=m; // 抵消
}
}
// 轮换修改
if(s[0][1]!=s[1][2] || s[1][2]!=s[2][0] || s[1][0]!=s[2][1] || s[2][1]!=s[0][2]) return -1;
if(s[0][1]!=0) return ans+2*s[0][1];
else return ans+2*s[1][0];
}
/*
int main(){
int n,q;string a,b;cin>>n>>q>>a>>b;
init(a,b);
while(q--){
int x,y;cin>>x>>y;
cout<<get_distance(x,y)<<endl;
}
return 0;
}
*/