1864A - Increasing and Decreasing
题意:给定x,y,n,其中要求构造一个数组a,其中满足a有n个元素,且起始元素为x,末尾元素为y,且a是一个严格单增的数组。同时满足数组b , 其中 , 要求b是严格单调递减的。
思路:直接从b开始构造,假设b的最后一个元素是1,往前依次递增1,求该情况下是多少,若 < x , 则代表无法满足。反之则能够满足。直接将改为x。其余不变即可。
void solve()
{
int x , y , n;
cin>>x>>y>>n;
vector<int>ans;
ans.pb(y);
for(int i = 1 ; i < n ; i ++){
y -= i ;
ans.pb(y);
}
if(ans[ans.size() - 1] >= x){
cout<<x;
for(int i = ans.size() - 2 ; i >= 0 ; i --){
cout<<" "<<ans[i];
}
}
else{
cout<<-1;
}
cout<<endl;
}
1864B - Swap and Reverse
题意:给定字符串s,一个整数k,其中能够进行两种操作。
1、交换。
2、翻转[i , i + k - 1]内的所有字符。
假设能进行无数次这两种操作,求最终字符串的字典序最小串。
思路:对于操作1而言,我们可以理解为奇数位排序和偶数位排序。如果说 k 奇数。那么翻转过后位于奇数位置上的字符仍然位于奇数,位于偶数上的字符仍然位于偶数。则只需要将奇数位偶数位分开排序即可。如果k是偶数,假如有字符串12345, k = 4 。 我们交换[1,4]之后变成了 43215 ,再一次交换[2,5]之后变成了45123。 这样就仅仅是4、5两个元素完成了奇偶对换。因此可以得出,只需要交换[i , i + k -1 ] 和 [i + 1 , i + k],就可以满足将任意一个奇数位上的数换成偶数位、偶数位上的数换成奇数位。最后在通过操作1,则完成了整体的排序。
void solve()
{
int n , k;
cin>>n>>k;
string s;
cin>>s;
if(k % 2 == 0){
int a[n];
for(int i = 0 ; i < n ; i++){
a[i] = s[i] - 'a';
}
sort(a , a + n , cmp);
char c;
for(int i = 0 ;i < n ; i++){
c = a[i] + 'a';
cout<<c;
}
}
else{
vector<int>a , b;
for(int i = 0 ; i < n ; i ++){
if(i % 2 == 0){
a.pb(s[i] - 'a');
}
else{
b.pb(s[i] - 'a');
}
}
sort(a.begin() , a.end() ,cmp);
sort(b.begin() , b.end() ,cmp);
char c;
int i = 0 , j = 0;
while(n){
c = a[i++] + 'a';
cout<<c;
n--;
if(n == 0){
break;
}
c = b[j++] + 'a';
cout<<c;
n--;
}
}
cout<<endl;
}
1864C - Divisor Chain
题意:给定数字a,每次操作将a减去a的其中一个因子。最终要求在1000次以内将a变为1,其中每个数最多能减两次。
思路:遇事不决二进制。考虑到二进制数 10010 ,他的因子必然包含了 , 那么减去之后就变成了 10000 , 之后如何变成 00001 呢 ?考虑到二进制的除法。 10000 的因子必然有 , 减去之后变成了 1000 , 以此类推,每次都将原数字除以二,最终就能变成1。 共有两轮操作,每轮中每位数最多操作1次,因此满足了每个数最多减2次。
void solve()
{
int n;
cin>>n;
int k = n;
vector<int>e;
int cnt1 = 0;
while(k){
e.pb(k % 2);
k /= 2;
}
vector<int>ans;
ans.pb(n);
for(int i = 0 ; i < e.size() ; i ++){
int d = i == 0 ? 1 : pow(2 , i);
if(e[i] == 1){
if(n > d){
n -= d;
ans.pb(n);
}
else{
break;
}
}
}
while(n > 1){
n/=2;
ans.pb(n);
}
cout<<ans.size()<<endl;
for(auto x : ans)
cout<<x<<" ";
cout<<endl;
}
1864D - Matrix Cascade
题意:给定一个n * n 的01网格,能够进行以下操作:选择一个[x,y] 的格子,将其自身和所有 的[ i , j ] 格子进行翻转。求将所有格子都变成0的最小操作数。
思路:可以想到,第一个含有1的行中,其 1 必然只能通过自己进行翻转。因此每一行翻转过后,继续找到第一个含有1的行进行翻转,这样子操作的话能保证操作数最小。
n * n 为 9e6 量级的数,因此每次翻转一个格子,将其下面的所有格子都翻转是不可能实现的。因此想到了线段树中的类似于懒标记的做法 : 记录一下翻转数在一行中的起点和终点。下放的过程当中,起点向左移动一格,终点向右移动一格。然后利用差分数组来求出每一个格子之前到底翻转了几次。再来判断是否需要翻转。
void solve()
{
int n;
cin>>n;
LL st[n];
LL en[n + 5];
LL a[n][n];
memset(st, 0 , sizeof st);
memset(en, 0 , sizeof en);
for(int i = 0 ; i < n ; i++){
string s;
cin>>s;
for(int j = 0; j < n ; j ++)
a[i][j] = s[j] - '0';
}
LL ans = 0;
for(int i = 0 ; i < n ; i ++){
LL cnt = 0;
vector<int>tmp;
st[0] += st[1];
for(int j = 1 ; j < n - 1; j ++){
st[j] = st[j + 1];
}
st[n - 1] = 0;
for(int j = n ; j > 0 ; j --){
en[j] = en[j - 1];
}
for(int j = 0 ; j < n ; j ++){
cnt += st[j] - en[j];
if((cnt + a[i][j]) % 2 == 1){
tmp.pb(j);
ans++;
}
}
for(auto x : tmp){
st[x] ++;
en[x + 1]++;
}
}
cout<<ans<<endl;
}
1864E - Guess Game
题意:有一个含有n个元素的数组,Alice 和 Bob 又在一起玩游戏了。其中Carol 作为裁判,每一次游戏会从数组中任意选择一个数a告诉Alice ,再选择一个数b告诉Bob , 再将 a | b 的结果告诉两人。其中Alice先玩,他需要说知道还是不知道,如果说知道,则需要给出 a 和 b 的大小关系,若不知道则轮到对方。求最优情况之下游戏轮数的期望。
思路:(我自己想的,不一定正确)假如说a = 10010 , b = 11010 , a | b = 11010 ,那么Alice会首先说不知道,因为她无法确定对方的第一位是否为1。然后轮到Bob了,此时他知道:如果Alice第一位是0,那么他必然会说a < b 。 然而他没有,因此Alice的第一位是1。然后又因为自己第二位是1,但是无法确定对方第二位是多少,因此他也会放弃。到了第三轮,Alice看透了Bob的想法,因此她也知道了Bob的前两个数是11 ,而他是10,所以他会直接说a < b。综上:每一轮假如对方说不知道,那么对方的这一位数一定是1,同时a | b 的这一位也是1。假如说 a | b = 10111011,那么第一轮过后,Bob / Alice已知Alice是10xxx0xx , 第二轮过后,Alice / Bob已知Bob是101xx0xx,以此类推。涉及到前缀的比较,应该是用Trie树来实现?