CF910 div2
CF910 div2
A.Milica and String
题意
给出一个只由大写字母‘A’和‘B’组成的字符串,要求用最少的操作次数使得字符串恰好含有k个‘B’。
操作定义为,选择一个位置k和一个字符c(c = ‘A’ || c = ‘B’),然后令所有的\(s_i ,(1 <= i <= k)\) 都变为c。
数据范围
多组数据,\(1 <= T <= 500 , 3 <= n <= 100 , 0 <= k <= n\)
题解
最多操作1次,多于1次的操作没有意义。
#include<bits/stdc++.h>
using namespace std;
int n , k;
char String[110];
int Solve()
{
int numB;
cin >> n >> k;
cin >> (String + 1);
numB = 0;
for(int i = 1 ; i <= n ; ++i)
numB += (String[i] == 'B');
if(numB == k)
{
cout << 0 << '\n';
}
else
if(numB < k)
{
cout << 1 << '\n';
for(int i = 1 ; i <= n ; ++i)
{
if(String[i] == 'A')
{
numB++;
if(numB == k)
{
cout << i << ' ' << 'B' << '\n';
break;
}
}
}
}
else
{
cout << 1 << '\n';
for(int i = 1 ; i <= n ; ++i)
{
if(String[i] == 'B')
{
numB--;
if(numB == k)
{
cout << i << ' ' << 'A' << '\n';
break;
}
}
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T; while(T--) Solve(); return 0;
}
B.Milena and Admirer
题意
给出n个数字,一次操作可以选择序列中的一个数字\(a_i\),令其变成\(x , a_i - x\),其中\(1 <= x < a_i\)。
问最少多少次操作可以使得序列成为不降序列。
数据范围
多组数据,\(1 <= n <= 2*10^5 , 1 <= a_i <= 10^9\)
输入样例
4
3
1 3 2
4
1 2 3 4
3
3 2 1
7
1 4 4 3 5 7 6
输出样例
1
0
3
9
题解
从后往前遍历,如果\(a_i <= a_{i+1}\)不必操作。反之,则需要将\(a_i\)分裂。
基于贪心思想,分裂完成之后最前方的一部分应该尽量大。
同时为保持序列不降,分裂出的每一部分都不能比后一部分大,最后一部分则不能比\(a_{i+1}\)大。
所以,\(a_i\)会分裂出\(\lceil a_i / a_{i+1}\rceil\)个数字,并且最前方的数字是\(\lfloor a_i / \lceil a_i / a_{i+1}\rceil \rfloor\)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n;
int A[N];
int Solve()
{
int lst , len;
long long Answer;
cin >> n;
for(int i = 1 ; i <= n ; ++i)
cin >> A[i];
Answer = 0ll; lst = A[n];
for(int i = n - 1 ; i >= 1 ; --i)
{
if(A[i] > lst)
{
len = (A[i] + lst - 1) / lst;
Answer += len - 1;
lst = min(lst , A[i] / len);
}
lst = min(lst , A[i]);
}
cout << Answer << '\n';
return 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T; while(T--) Solve(); return 0;
}
C.Colorful Grid
题意
要求对n*m的格点图的边进行红蓝染色,使得染色完成后的格点图上存在条满足如下要求的路径。
- 路径由边组成,不会连续经过相同颜色的两条边。
- 路径长度为k。
- 起点为(1,1),终点为(n,m)
路径可以重复经过点或者边。
数据范围
多组数据,\(3 <= n , m <= 16 , 1 <= k <= 10^9\)
输入样例
5
4 5 11
3 3 2
3 4 1000000000
3 3 12588
4 4 8
输出样例
YES
R R B B
R R R R
B B B R
R R B B
R B B R B
R B B B B
B B R R R
NO
NO
YES
R B
B B
B R
B B R
R B B
YES
B B R
R B R
B R R
R R B
B R R B
B B B B
B R R R
题解
从(1,1)到(n,m)的路径的最短长度为(n-1)+(m-1)。
易得不论路径如何变化,路径的长度的奇偶性总是不变的。
那么按照%4的结果划分,如果余数为1或者是3,那么就无解。
如果余数是0,则可以将多余的部分用来围绕一个1*1的方块绕圈子。
如果余数是2,可以通过先下再右再上(或类似操作)消耗掉2步,然后再绕圈子。
#include<bits/stdc++.h>
using namespace std;
int n , m , k;
int rows[20][20] , cols[20][20];
int Solve()
{
int dis;
cin >> n >> m >> k;
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m ; ++j)
rows[i][j] = cols[i][j] = -1;
dis = (n - 1) + (m - 1);
if(k < dis || (k - dis) % 4 == 1 || (k - dis) % 4 == 3)
cout << "NO" << '\n';
else
{
cout << "YES" << '\n';
if((k - dis) % 4 == 0)
{
rows[1][1] = rows[2][1] = 1;
cols[1][1] = cols[2][1] = 0;
for(int i = 2 ; i <= n - 1 ; ++i)
cols[1][i] = !cols[1][i-1];
rows[n][1] = !cols[1][n-1];
for(int i = 2 ; i <= m - 1 ; ++i)
rows[n][i] = !rows[n][i-1];
}
else
{
rows[1][1] = rows[2][1] = 1;
cols[1][1] = cols[2][1] = 0;
rows[1][2] = 1;
for(int i = 3 ; i <= m - 1 ; ++i)
rows[1][i] = !rows[1][i-1];
cols[m][1] = !rows[1][m-1];
for(int i = 2 ; i <= n - 1 ; ++i)
cols[m][i] = !cols[m][i-1];
}
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m - 1 ; ++j)
{
if(rows[i][j] != -1)
cout << (rows[i][j] ? 'R' : 'B');
else
cout << 'R';
if(j == m - 1)
cout << '\n';
else
cout << ' ';
}
for(int j = 1 ; j <= n - 1 ; ++j)
for(int i = 1 ; i <= m ; ++i)
{
if(cols[i][j] != -1)
cout << (cols[i][j] ? 'R' : 'B');
else
cout << 'R';
if(i == m)
cout << '\n';
else
cout << ' ';
}
}
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T; while(T--) Solve(); return 0;
}
D.Absolute Beauty
题意
有两个序列,{\(a_i\)} , {\(b_i\)},可以交换最多一次b序列中的两个数字,要求最大化
问最大的结果是多少?
数据范围
多组数据,\(1 <= n <= 2*10^5 , 1 <= a_i , b_i <= 10^9\)
输入样例
6
3
1 3 5
3 3 3
2
1 2
1 2
2
1 2
2 1
4
1 2 3 4
5 6 7 8
10
1 8 2 5 3 5 3 1 1 3
2 9 2 4 8 2 3 5 3 1
3
47326 6958 358653
3587 35863 59474
输出样例
4
2
2
16
31
419045
题解
将一对\((a_i , b_i)\)看做区间,那么问题就是区间之和最大。
对于交换操作,可以根据做交换的两个区间的不相交,包含,相交三种情况进行讨论。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n;
int A[N] , B[N];
inline int Abs(int x) { return x < 0 ? -x : x; }
long long CalcAA()
{
int Max = 1 , Min = 1;
long long tmp = 0ll;
for(int i = 1 ; i <= n ; ++i)
{
if(A[i] > A[Max])
Max = i;
if(A[i] < A[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
return tmp;
}
long long CalcAB()
{
int Max = 1 , Min = 1;
long long tmp1 = 0ll , tmp2 = 0ll;
for(int i = 1 ; i <= n ; ++i)
{
if(A[i] > A[Max])
Max = i;
if(B[i] < B[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp1 += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
{
if(B[i] > B[Max])
Max = i;
if(A[i] < A[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp2 += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
return max(tmp1 , tmp2);
}
long long CalcBB()
{
int Max = 1 , Min = 1;
long long tmp = 0ll;
for(int i = 1 ; i <= n ; ++i)
{
if(B[i] > B[Max])
Max = i;
if(B[i] < B[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
return tmp;
}
long long Calc4()
{
int Max , Min;
long long tmp = 0ll , Answer = 0ll;
Max = Min = 1; tmp = 0ll; // 1
for(int i = 1 ; i <= n ; ++i)
{
if(A[i] - B[i] > A[Max] - B[Max])
Max = i;
if(A[i] - B[i] < A[Min] - B[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
Answer = max(Answer , tmp);
Max = Min = 1; tmp = 0ll; // 2
for(int i = 1 ; i <= n ; ++i)
{
if(B[i] - A[i] > B[Max] - A[Max])
Max = i;
if(B[i] - A[i] < B[Min] - A[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
Answer = max(Answer , tmp);
Max = Min = 1; tmp = 0ll; // 3
for(int i = 1 ; i <= n ; ++i)
{
if(A[i] + B[i] - Abs(A[i] - B[i]) > A[Max] + B[Max] - Abs(A[i] - B[i]))
Max = i;
if(A[i] + B[i] + Abs(A[i] - B[i]) < A[Min] + B[Min] + Abs(A[i] - B[i]))
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
Answer = max(Answer , tmp);
Max = Min = 1; tmp = 0ll; // 4
for(int i = 1 ; i <= n ; ++i)
{
if(A[i] - B[i] > A[Max] - B[Max])
Max = i;
if(B[i] - A[i] < B[Min] - A[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
Answer = max(Answer , tmp);
Max = Min = 1; tmp = 0ll; // 5
for(int i = 1 ; i <= n ; ++i)
{
if(B[i] - A[i] > B[Max] - A[Max])
Max = i;
if(A[i] - B[i] < A[Min] - B[Min])
Min = i;
}
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
swap(B[Max] , B[Min]);
Answer = max(Answer , tmp);
return Answer;
}
int Solve()
{
int Max , Min;
long long Answer = 0ll , tmp = 0ll;
cin >> n;
for(int i = 1 ; i <= n ; ++i)
cin >> A[i];
for(int i = 1 ; i <= n ; ++i)
cin >> B[i];
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
Answer = max(Answer , tmp);
for(int i = 1 ; i <= n ; ++i)
if(A[i] > B[i]) swap(A[i] , B[i]);
Max = Min = 1;
for(int i = 1 ; i <= n ; ++i)
{
if(A[Max] < A[i])
Max = i;
if(B[Min] > B[i])
Min = i;
}
tmp = 0ll;
swap(B[Max] , B[Min]);
for(int i = 1 ; i <= n ; ++i)
tmp += Abs(A[i] - B[i]);
Answer = max(Answer , tmp);
cout << Answer << '\n';
return 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T; while(T--) Solve(); return 0;
}
/*
20
27 45 23 47 31 25 24 23 44 99 23 72 56 36 89 4 16 54 39 9
75 20 92 78 54 62 2 91 45 88 98 3 65 52 28 41 19 60 84 52
199 215
10
6 15 25 8 4 17 22 16 1 9
47 17 30 37 40 40 16 40 24 15
a1 - b1 a2 - b2
a1 - b2 b1 - a2
21 10
3 10
*/
E.Sofia and Strings
题意
给出两个字符串S,T,要求经过若干次操作之后将S变为T。
操作有两种:
- 删除一个字符
- 选择一段区间,将其中字符按照字典序从小到大排序。
问是否可行。
数据范围
多组数据,\(1 <= m <= n <= 2*10^5\)
输入样例
8
5 5
sofia
afios
3 2
cba
bc
5 1
sofia
e
15 7
anavolimilovana
aamanan
26 4
abcdefghijklmnopqrstuvwxyz
nope
26 4
zyxwvutsrqponmlkjihgfedcba
nope
7 3
apricot
cat
3 3
cba
acb
输出样例
YES
YES
NO
YES
NO
YES
NO
YES
题解
用双指针同时遍历原串和目标串,i表示原串遍历到的位置,j表示目标串遍历到的位置。
在原串中寻找 \(s_k (s_k == t_j\ \ and\ \ k >= i)\) ,基于贪心思想,k应尽量的小。
并且,题目中对于操作次数没有限制,所以排序时可以只选择一个长度为2的区间进行排序。
这就说明\(s_k\)前方的字典序比\(s_k\)大的不会阻挡其向前移动。
但是那些比\(s_k\)字典序小的就需要一个一个删掉才行。
为了模拟这个过程,设计一个leftmin数组,如果有找到\(s_k\),就令i~k之间的所有leftmin都和\(s_k\)取max。
如果一个字符本身小于其对应的leftmin数组,这就说明它已经被删掉了。
易得,leftmin数组在i之后应当是单调不增的,而且其值域大小只有26,所以可以暴力操作。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n , m;
char str1[N] , str2[N];
int leftmin[N];
set<int>Set[30];
bool Check()
{
for(int i = 1 ; i <= m ; ++i)
{
int t;
if(Set[str2[i] - 'a'].empty()) return false;
do{
t = *Set[str2[i] - 'a'].begin();
Set[str2[i] - 'a'].erase(Set[str2[i] - 'a'].begin());
}while(leftmin[t] > str2[i] - 'a' && !Set[str2[i] - 'a'].empty());
if(leftmin[t] > str2[i] - 'a') return false;
while(t >= 1 && str2[i] - 'a' > leftmin[t])
leftmin[t--] = str2[i] - 'a';
}
return true;
}
int Solve()
{
cin >> n >> m;
for(int i = 0 ; i < 26 ; ++i) Set[i].clear();
for(int i = 1 ; i <= n ; ++i) leftmin[i] = -1;
cin >> (str1+1) >> (str2+1);
for(int i = 1 ; i <= n ; ++i)
Set[str1[i] - 'a'].insert(i);
cout << (Check() ? "YES" : "NO") << '\n';
return 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T; while(T--) Solve(); return 0;
}
F.Vova Escapes the Matrix
题意
有一个n*m的迷宫,迷宫中有墙'#',有空地'.',有起点'V'。迷宫的边界认为是出口,并且迷宫按照出口的数量分为3中类型。
- 没有出口
- 有一个出口
- 有多个出口
现在可以将一些空地(不包括起点)变为墙,但是要求迷宫的类型不能改变,请问最多可以修改多少空地使之变成墙。
数据范围
多组数据,\(3 <= n , m <= 1000\)
输入样例
8
4 4
#..#
..V.
....
#..#
3 6
#.####
#....#
####V#
3 3
###
#V#
###
6 5
#.###
#...#
###.#
#V..#
#.###
##..#
7 5
#####
#.V.#
#.#.#
#.#.#
#.#.#
#...#
#.#.#
3 7
#.....#
.#####.
#...V.#
5 8
####.###
#..V#..#
#...#..#
#...##.#
###.####
5 5
#...#
##.##
##V##
##.##
#...#
输出样例
9
0
0
3
4
10
12
5
题解
- 没有出口
直接将所有空地都变成墙 - 有一个出口
求到出口的最短路,然后保留这条路径,其余空地变成墙。
转化到数值上来看的话就是总空地数目-最短路长度。
这里的最短路可以用bfs解决。 - 有多个出口
最终应保留两个出口,结果就是总空地数目-两条路径长度。
问题在于这两条路径可能部分重合。
先求出任意两个出口到(i,j)位置的路径长度之和(重叠部分算两次,但这并不影响,因为最优解一定是选择了没有重叠部分的两条路径之和)。
具体做法是,对于每个点记录3个数据sum表示路径长度的和,time表示被几个出口访问到,lst表示上一个访问到这个位置的出口是哪个。
将所有的出口一起当做源点进行bfs,并求出上述3个数据。
再之后对于每个点再统计上从起点到该点的距离,然后统计最小值。
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n , m;
const int dx[] = {0 , 0 , 1 , -1};
const int dy[] = {1 , -1 , 0 , 0};
int Last[N][N] , Time[N][N] , Sum[N][N] , Dist[N][N];
char Map[N][N];
int Check()
{
int num = 0 , x , y , a , b;
queue<pair<int,int>> Q;
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m ; ++j)
if(Map[i][j] == 'V')
Dist[i][j] = 0 , Q.push(make_pair(i , j));
else
Dist[i][j] = -1;
while(!Q.empty())
{
x = Q.front().first , y = Q.front().second; Q.pop();
if(x == 1 || y == 1 || x == n || y == m) num++;
for(int k = 0 ; k < 4 ; ++k)
{
a = x + dx[k]; b = y + dy[k];
if(a < 1 || a > n || b < 1 || b > m) continue;
if(Map[a][b] == '#' || Dist[a][b] != -1) continue;
Dist[a][b] = Dist[x][y] + 1;
Q.push(make_pair(a , b));
}
}
return num;
}
int Solve1()
{
int num = 0;
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m ; ++j)
if(Map[i][j] == '.') num++;
cout << num << '\n'; return 0;
}
int Solve2()
{
int num = 0 , dist;
for(int i = 1 ; i <= n ; ++i)
{
if(Dist[i][1] != -1) { dist = Dist[i][1]; break; }
if(Dist[i][m] != -1) { dist = Dist[i][m]; break; }
}
for(int i = 1 ; i <= m ; ++i)
{
if(Dist[1][i] != -1) { dist = Dist[1][i]; break; }
if(Dist[n][i] != -1) { dist = Dist[n][i]; break; }
}
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m ; ++j)
if(Map[i][j] == '.') num++;
cout << num - dist << '\n'; return 0;
}
struct Node{
int x , y , dist , tag;
Node(int x = 0 , int y = 0 , int dist = 0 , int tag = 0): x(x) , y(y) , dist(dist) , tag(tag) {}
};
int Solve3()
{
int Answer , num;
queue<Node> Q;
for(int i = 1 ; i <= n ; ++i)
{
if(Map[i][1] != '#') Q.push(Node(i , 1 , 0 , Last[i][1] = (i - 1) * m + 1)) , Time[i][1] = 1;
if(Map[i][m] != '#') Q.push(Node(i , m , 0 , Last[i][m] = i * m)) , Time[i][m] = 1;
}
for(int i = 1 ; i <= m ; ++i)
{
if(Map[1][i] != '#') Q.push(Node(1 , i , 0 , Last[1][i] = i)) , Time[1][i] = 1;
if(Map[n][i] != '#') Q.push(Node(n , i , 0 , Last[n][i] = (n - 1) * m + i)) , Time[n][i] = 1;
}
while(!Q.empty())
{
Node t , r;
t = Q.front(); Q.pop();
for(int k = 0 ; k < 4 ; ++k)
{
r = Node(t.x + dx[k] , t.y + dy[k] , t.dist + 1 , t.tag);
if(r.x < 1 || r.x > n || r.y < 1 || r.y > m) continue;
if(Map[r.x][r.y] == '#' || Time[r.x][r.y] == 2 || Last[r.x][r.y] == r.tag) continue;
Time[r.x][r.y]++;
Last[r.x][r.y] = r.tag;
Sum[r.x][r.y] += r.dist;
// if(r.x == 7 && r.y == 2) cout << "test:" << r.dist << ' ' << r.tag << '\n';
Q.push(r);
}
}
Answer = 1e8; num = 0;
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m ; ++j) if(Dist[i][j] != -1 && Time[i][j] == 2)
Answer = min(Answer , Dist[i][j] + Sum[i][j]);
// for(int i = 1 ; i <= n ; ++i)
// for(int j = 1 ; j <= m ; ++j)
// cout << Sum[i][j] << (j == m ? '\n' : ' ');
// for(int i = 1 ; i <= n ; ++i)
// for(int j = 1 ; j <= m ; ++j)
// cout << Time[i][j] << (j == m ? '\n' : ' ');
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m ; ++j) if(Map[i][j] == '.') num++;
cout << num - Answer << '\n';
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= m ; ++j)
Dist[i][j] = Last[i][j] = Time[i][j] = Sum[i][j] = 0;
return 0;
}
int Solve()
{
int opt;
cin >> n >> m;
for(int i = 1 ; i <= n ; ++i)
cin >> (Map[i] + 1);
opt = Check();
if(opt == 0) Solve1();
else
if(opt == 1) Solve2();
else Solve3();
return 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T; while(T--) Solve(); return 0;
}