CF910 div2

CF910 div2

A.Milica and String

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

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

C.Colorful Grid-1
C.Colorful Grid-2
题意
要求对n*m的格点图的边进行红蓝染色,使得染色完成后的格点图上存在条满足如下要求的路径。

  1. 路径由边组成,不会连续经过相同颜色的两条边。
  2. 路径长度为k。
  3. 起点为(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

D.Absolute Beauty
题意
有两个序列,{\(a_i\)} , {\(b_i\)},可以交换最多一次b序列中的两个数字,要求最大化

\[\sum_{i=1}^n |a_i - b_i| \]

问最大的结果是多少?
数据范围
多组数据,\(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)\)看做区间,那么问题就是区间之和最大。
对于交换操作,可以根据做交换的两个区间的不相交,包含,相交三种情况进行讨论。
img

#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

E.Sofia and Strings
题意
给出两个字符串S,T,要求经过若干次操作之后将S变为T。
操作有两种:

  1. 删除一个字符
  2. 选择一段区间,将其中字符按照字典序从小到大排序。

问是否可行。
数据范围
多组数据,\(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

F.Vova Escapes the Matrix
题意
有一个n*m的迷宫,迷宫中有墙'#',有空地'.',有起点'V'。迷宫的边界认为是出口,并且迷宫按照出口的数量分为3中类型。

  1. 没有出口
  2. 有一个出口
  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

题解

  1. 没有出口
    直接将所有空地都变成墙
  2. 有一个出口
    求到出口的最短路,然后保留这条路径,其余空地变成墙。
    转化到数值上来看的话就是总空地数目-最短路长度。
    这里的最短路可以用bfs解决。
  3. 有多个出口
    最终应保留两个出口,结果就是总空地数目-两条路径长度。
    问题在于这两条路径可能部分重合。
    先求出任意两个出口到(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;
}
posted @ 2023-11-29 10:08  沙野博士  阅读(17)  评论(0编辑  收藏  举报