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;
}
posted @ 2023-01-23 17:27  SFlyer  阅读(136)  评论(0编辑  收藏  举报