ARC 154 做题笔记
现在只做了 A~C,会 D 了以后再更。
A
题目大意:
有两个长度为 \(N\) 的整数,\(A\) 和 \(B\)。设 \(A_i\) 为 \(A\) 从高到低第 \(i\) 位,\(B_i\) 同理。现在可以对于每一个 \(i\) 选择交不交换 \(A_i\) 和 \(B_i\),求 \(\min(A\times B)\)。
Hint 1
\(A+B\) 变吗?
Answer for Hint 1
不变。
Hint 2
你上过小学吗?
Tutorial
小学里学过,和定差小积大,说明和定差大积小。所以我们只需要让 \(A\) 尽可能小,\(B\) 尽可能大(或者让 \(B\) 尽可能小,\(A\) 尽可能大)就可以了。也就是说若 \(A_i>B_i\),交换 \(A_i\) 和 \(B_i\)。
和定差小积大 无聊的证明
令 \(S=A+B\),则 \(B=S-A\),\(\displaystyle A\times B=A(S-A)=-A^2+SA=-(A-\frac{S}{2})^2+\frac{S^2}{4}\)。由 \(\displaystyle -(A-\frac{S}{2})^2\le 0\),则取 \(\displaystyle A=\frac{S}{2}=B\),\(A\times B\) 最大,为 \(\displaystyle \frac{S^2}{4}\)。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
string a,b;
cin>>a>>b;
for (int i=0; i<n; i++){
if (a[i]>b[i]){
swap(a[i],b[i]);
}
}
ll A=0,B=0;
for (int i=0; i<n; i++){
A=A*10ll+a[i]-'0';
A%=998244353;
B=B*10ll+b[i]-'0';
B%=998244353;
}
cout<<A*B%998244353<<endl;
return 0;
}
B
题目大意:
给定两个长度为 \(N\) 的字符串 \(S\),\(T\)。你可以进行任意次操作,每一次操作为:
- 选取 \(S\) 的第一个字符,插入到 \(S\) 的任意一个位置.
求至少多少操作使得 \(S=T\)。若没有,输出 \(-1\)。
Hint 1
怎么判断 \(-1\)?
Answer for Hint 1
排序 \(S\) 和 \(T\),查看是否相同,若不相同输出 \(-1\)。
Hint 2
是第一个字符。假如你操作了 \(k\) 次,第 \(k+1\) 到 \(N\) 个字符(最优情况下)会改变顺序吗?
Answer for Hint 2
不会。
Hint 3
若答案是 \(k\),要满足什么性质?
Hint 4
答案满足单调性吗?
Tutorial
注意到若答案是 \(k\),我们重新定义操作为:
-
设 \(X\) 为 \(S\) 从 \(k+1\) 到 \(N\) 的连续子串。
-
前 \(k\) 个字符可以任意插到 \(X\) 中。
所以,\(X\) 一定是 \(T\) 的子串。我们可以二分 \(k\),\(O(N)\) 判断,因为答案满足单调性。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
string s,t;
cin>>s>>t;
string S=s,T=t;
sort(S.begin(),S.end());
sort(T.begin(),T.end());
if (S!=T){
cout<<-1<<endl;
return 0;
}
int l=-1,r=n+1;
while (l+1<r){
int mid=l+r>>1;
int i=mid,j=0,cnt=0;
while (i<n && j<n){
if (s[i]==t[j]){
i++;
j++;
cnt++;
}
else{
j++;
}
}
if (cnt==n-mid){
r=mid;
}
else{
l=mid;
}
}
cout<<r<<endl;
return 0;
}
C
题目大意:
给出长度为 \(N\) 的序列 \(A=\{A_1,A_2,\cdots, A_N\},B=\{B_1,B_2,\cdots , B_N\}\)。可以进行任意次操作:
- 选择 \(i\),\(A_i\leftarrow A_{i+1}\)。
问是否能使 \(A=B\)?
Hint 1
若 \(A=B\),答案是 \(\tt{Yes}\)。
Hint 2
尝试压缩 \(B\)。
Hint 2.2
压缩以后答案不会改变。
Hint 3
\(1\) 次操作后,\(A\) 里一定会有一个 \(i\) 满足 \(A_i=A_{i+1}\)。
Hint 4
这些操作可以让 \(A\) 平移吗?
Answer for Hint 4
可以。可以左移。尝试根据 Hint 3 证明。
Hint 5
一个 \(A\) 的平移怎么判断可不可以达到 \(B\)?
Answer for Hint 5
当且仅当 \(B\) 是平移后 \(A\) 的子序列。
Hint 6
这题可以 \(O(N^2)\)。
Tutorial
感觉 Hint 足够了。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e4+4;
int n,a[N],b[N],c[N],cnt;
void you_should_compress(){
cnt=0;
c[cnt++]=b[0];
for (int i=1; i<n; i++){
if (b[i-1]!=b[i]){
c[cnt++]=b[i];
}
}
if (c[cnt-1]==c[0] && cnt!=1){
cnt--;
}
for (int i=0; i<cnt; i++){
b[i]=c[i];
}
}
bool issubstr(int pos){
int i=pos,j=0;
while (i<n+pos && j<cnt){
if (a[i]==b[j]){
j++;
}
else{
i++;
}
}
return j==cnt;
}
void solve(){
cin>>n;
for (int i=0; i<n; i++){
cin>>a[i];
a[i+n]=a[i];
}
for (int i=0; i<n; i++){
cin>>b[i];
}
bool same=1;
for (int i=0; i<n; i++){
same&=(a[i]==b[i]);
}
if (same){
cout<<"Yes"<<endl;
return;
}
set<int> st;
for (int i=0; i<n; i++){
st.insert(b[i]);
}
if (st.size()==n){
cout<<"No"<<endl;
return;
}
you_should_compress();
if (cnt==n){
cout<<"No"<<endl;
return;
}
for (int i=0; i<n; i++){
if (issubstr(i)){
cout<<"Yes"<<endl;
return;
}
}
cout<<"No"<<endl;
return;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
solve();
}
return 0;
}