9.14做题记录
思维题
蒟蒻做思维题的第二天。
越来越觉得自己是个 sb 了
CF1009B Minimum Ternary String
题面翻译
给定一个由 '0', '1', '2' 组成的字符串 \(S\) 。可以交换相邻'0', '1'或'1', '2'的位置(例如:'12' - '21' \(\;\) '01' - '10')请输出原字符串经过任意转换后字典序最小的字符串。原字符串长度不超过 \(10^5\) 。
输入格式
字符串 \(S\)
输出格式
转化后字典序最小的字符串
样例 #1
样例输入 #1
100210
样例输出 #1
001120
样例 #2
样例输入 #2
11222121
样例输出 #2
11112222
样例 #3
样例输入 #3
20
样例输出 #3
20
\(solution\)
思维题。
根据题意我们能知道,\(1\) 可以和 \(2\) 交换位置,\(0\) 可以和 \(1\) 交换位置,只有这 \(2\) 种交换方式。
我们可以注意到, \(1\) 可以和其他 \(2\) 个数中的任意 \(1\) 个数交换位置,所以 \(1\) 可以在序列中的任何位置。
因为 \(0\) 不能和 \(2\) 交换位置,所以 \(0\) 和 \(2\) 的相对位置不会发生改变,所以字典序的大小仅由 \(1\) 的位置决定。
为了使字典序最小,我们很明显要把所以的 \(1\) 都放在第一个出现的 \(2\) 前面。
统计出 \(1\) 的个数,按照上面说的操作即可。
\(code\)
#include<iostream>
using namespace std;
const int N=1e5+5;
string s;
int cnt,arr,flag;
int main()
{
cin>>s;
int len=s.length();
for(int i=0;i<len;++i)
if(s[i]=='1') arr++;
for(int i=0;i<len;++i)
{
if(s[i]=='1') continue;
if(s[i]=='2'&&!flag)
{
for(int j=1;j<=arr;++j) cout<<"1";
cout<<"2",flag=1;
}
else cout<<s[i];
}
if(!flag) for(int i=1;i<=arr;++i) cout<<"1";
return 0;
}
CF986B Petr and Permutations
题面翻译
Petr要打乱排列。他首先有一个从 \(1\) 到 \(n\) 的顺序排列,然后进行 \(3n\) 次操作,每次选两个数并交换它们。
Alex也要打乱排列。他与Petr唯一的不同是他进行 \(7n+1\) 次操作。
给定一个 \(1\) 到 \(n\) 的排列。问是由谁打乱的。如果是Petr,输出"Petr",否则输出"Um_nik"(不是Alex)
样例 #1
样例输入 #1
5
2 4 5 1 3
样例输出 #1
Petr
\(solution\)
本蒟蒻第一道自己想出思路的思维题,有点激动
看到 \(3n\) 和 \(7n+1\) ,本蒟蒻的第一反应就是考虑奇偶性的问题。
如果 \(n\) 为偶数,那么 \(3n\) 为偶数, \(7n+1\) 为奇数。
如果 \(n\) 为偶数,那么 \(3n\) 为奇数, \(7n+1\) 为偶数。
也就是说, \(3n\) 和 \(7n+1\) 的奇偶性一定不同,且都由 \(n\) 决定。
然后我们考虑根据这一性质解决问题。
可以想到,我们最多只需要 \(n\) 次就能把调换后的序列恢复成原序列。
那么多余的次数呢???
肯定是两个数或者多个数之间来回换,次数增加偶数次,但是序列没变。
也就是说,我们只需要求出调换后的序列恢复成原序列需要多少步,然后判断剩余的步数是不是偶数即可。
当操作次数 \(cnt\) 与 \(n\) 同奇偶时,剩余次数为偶数,所以秩序暴力求出操作次数,然后判断奇偶性即可。
\(code\)
#include<iostream>
using namespace std;
const int N=1e6+5;
int a[N];
int n,cnt;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
while(a[i]!=i) swap(a[a[i]],a[i]),cnt++;
if( (cnt&1) == (n&1) ) printf("Petr");
else printf("Um_nik");
return 0;
}