Bootstrap

Codeforces Round 806 (Div. 4)题解ABCDEFG

Problem - A - Codeforces

题意:

给定一个字符串 s ,它的长度为 3 ,由大写字母和小写字母组成。

要求判断这个字符串是否为YES(不分大小写)。

例如yESYesyes都是合法的。

思路:模拟

    char a,b,c;
	cin >> a >> b >> c;
	bool f = false;
	if(a == 'Y'||a=='y'){
		if(b =='E'||b=='e'){
			if(c=='S'||c=='s'){
				f = true;
			}
		}
	}
	if(f)cout <<"yes"<<endl;
	else cout <<"no"<<endl;

Problem - B - Codeforces

题意:

在一场比赛中,有 26 道题目和很多个团队,题目标号分别为 A 到 Z。每当一道题目被第一次解决,该团队获得 2 个气球,否则获得 1 个气球。

现在给出一个字符串,代表题目被解决的顺序,字符串第 i 个字符 Si​ 代表题目 Si​ 被解决。求所有团队获得的气球总数。

思路:判断每个题目是不是第一次第一次解决,然后出现了几次。模拟

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		map<char,bool> vis;
		int cnt = 0;
		int n;
		cin >> n;
		string s;
		cin >> s;
		for(auto op : s){
			if(!vis[op]){
				vis[op] = true;
				cnt+=2;
			}
			else cnt += 1;
		}
		cout << cnt << endl;
		
	}

Problem - C - Codeforces

题意:

有一把 n 个锁轮的锁,每个锁轮上写有数字 ai​ ,对于第 i 个锁轮,做了 bi​ 次操作。这些操作有两种类型:

  • U:锁轮上的数字 +1 ,超过 9 则变为 0 。
  • D:锁轮上的数字 −1 ,小于 0 则变为 9 。

现在给定操作后的锁轮和操作,请你还原操作前的锁轮。

思路:考虑题目定义的进位的性质,发现只需要在0 和 10 的情况改为位即可。

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		vector<int> a(n);
		for(int i = 0;i<n;i++)cin >>a[i];
		for(int i = 0;i<n;i++){
			int tt;
			cin >> tt;
			string s;
			cin >> s;
			for(auto j :s){
				if(j == 'D')a[i]++;
				else a[i]--;
				
				if(a[i] >= 10)a[i] -= 10;
				else if(a[i] < 0)a[i] += 10;
			}
		}
		for(int i = 0;i<n;i++)cout <<a[i] <<" ";
		cout << endl;
	}

Problem - D - Codeforces

题意:给出 n 个字符串 s1​,s2​⋯sn​ 每个字符串最多长 8,问对于每个 si​(1≤i≤n) ,是否存在两个字符串 sj​,sk​(1≤j,k≤n)(j 可能等于 k)使得 si​=sj​+sk​ ,即 si​ 可以由 sj​,sk​ 拼接得到。若存在,输出 1,否则输出 0。共 t 组数据。

思路:观察到 n < 1e5所以遍历显然超时,思考优化方式,发现两个字符串拼接方式只可能是a + b 或者b + a,只需要判断是否能作为子串出现即可,联想到可以判断两个子串是否在原始字符串群组里出现,分割然后遍历出现即可

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		vector<string> v(n);
		map<string,bool> vis;
		for(int i = 0;i<n;i++){
			cin >> v[i];
			vis[v[i]] = true;
		}
		vector<int> ans(n,0);
		for(int i = 0;i<n;i++){
			for(int j = 0;j<sz(v[i]);j++){
				if(vis[v[i].substr(0,j+1)] && vis[v[i].substr(j+1,sz(v[i])-j-1)]){
					ans[i] = 1;
				}
			}
		}
		for(int i = 0;i<n;i++)cout << ans[i];
		cout << endl;
	}

Problem - E - Codeforces

题意:给定一个 n×n (n≤100) 的 01 矩形,求至少修改多少次后能使矩形旋转 0°,90°,180°,270°后所形成的矩形都完全相同。

思路:我们发现对于矩阵旋转四次相同,即:每一位在操作以后形成的新位置上能保持原有的性质,只需要保证原来那个和他对应的即可,总共四个(除了中心以外最多四个),遍历一下然后看0多还是1多,把少的那个加入总操作次数即可

	ans = 0; 
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int z = 1; z <= n; z++) {
			cin >>ch;
			a[i][z] = ch - '0';
		}
	}
	cnt1 = cnt2 = 0;
	for (int i = 1; (i << 1) <= n; i++) { 
		for (int z = 1; (z << 1) <= n; z++) {
			cnt1 = cnt2 = 0; 
			if (a[i][z] == 1) { 
				cnt1++;
			} else {
				cnt2++;
			}
			if (a[n - z + 1][i] == 1) {
				cnt1++;
			} else {
				cnt2++;
			}
			if (a[n - i + 1][n - z + 1] == 1) {
				cnt1++;
			} else {
				cnt2++;
			}
			if (a[z][n - i + 1] == 1) {
				cnt1++;
			} else {
				cnt2++;
			}
			ans += min(cnt1, cnt2);
		}
	}
	cnt1 = cnt2 = 0;
	if (n & 1) {
		for (int i = 1; (i << 1) <= n; i++) {
			cnt1 = cnt2 = 0;
			if (a[i][n / 2 + 1] == 1) {
				cnt1++;
			} else {
				cnt2++;
			}
			if (a[n / 2 + 1][n - i + 1] == 1) {
				cnt1++;
			} else {
				cnt2++;
			}
			if (a[n / 2 + 1][i] == 1) {
				cnt1++;
			} else {
				cnt2++;
			}
			if (a[n - i + 1][n / 2 + 1] == 1) {
				cnt1++;
			} else {
				cnt2++;
			}
			ans += min(cnt1, cnt2); 
		}
	}
	cout << ans << endl; 

Problem - F - Codeforces

题意:给你一个序列a1​,a2​,…an​ 。请计算出满足下面条件的 (i,j)(1≤i,j≤n)个数 。

  • ai​<i<aj​<j 

思路:在最理想的情况,即:序列为0 1 2 3 4 5的情况,我们显然发现对于序列每一位所对应的“贡献”显然递增,即答案呈单调性。

因此我们可以遍历数组,记录一下当前的贡献,如果满足题意则根据上一个 + 1,否则维持原态,对于当前遍历到的再加入能加入贡献的最小值,即i - 1即可

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		vector<int> a(n+1);
		vector<int> s(n+1,0);
		int ans = 0;
		for(int i = 1;i<=n;i++)cin >> a[i];
		for(int i = 1;i<=n;i++){
			if(a[i] < i) s[i] = s[i-1] + 1;
			else s[i] = s[i-1];
			
			if(a[i] >= 1 && a[i] < i) ans += s[a[i] - 1];
		}
		cout << ans << endl;
	}

Problem - G - Codeforces

题意:

你有 n 个箱子。第 i 个箱子中有 ai​ 个硬币。你需要按照从箱子 1 号到箱子 n 号的顺序打开所有 n 个箱子。

你可以用以下两种钥匙之一打开一个箱子:

  • 好钥匙:使用一次消耗 k 个硬币。
  • 坏钥匙:使用时不消耗硬币,但会使所有未打开的箱子中的硬币数减半(包括正要打开的这个箱子)。硬币减半时向下取整。比如,用坏钥匙打开箱子 i 号时,ai​=ai​/2,ai+1​=ai+1​/2,......,an​=an​/2。

所有钥匙用过一次就会断掉(别想着买一把好钥匙开完所有箱子了),好钥匙需要重复付费,坏钥匙效果会重复计算。

也就是说,你总共需要使用 n 把钥匙,每个箱子用一把。开始时,你没有硬币和钥匙,如果想用好钥匙,你就得去买。值得注意的是,在这个过程中你可以赊账买钥匙;例如,如果你只有 1 个硬币,你也可以购买价值 k=3 个硬币的好钥匙,你的余额会变成 −2 个硬币。

你需要求出开完所有箱子之后你能获得的最大硬币数量

思路:

观察到每次使用坏钥匙在使用时会从当前开始(含)对后续的每个产生负的贡献,所以优先使用好钥匙。当不足以支付且后面/2的贡献大于K时,连续使用坏钥匙。

观察到每次从第i个开始使用坏钥匙的原始贡献是第i个之前的  所以可以采用前缀和来计算。

每次使用坏钥匙的次数不会大于 log2(MAX_subsquence)次,因为最大值/2这些次数会变成0,所以比他小的最大会产生这些贡献,时间复杂度O30 * n可以接受

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		int n , k;
		cin >> n >> k;
		vector<int> a(n+1);
		vector<int> s(n+1);
		int max_val = -LINF;
		for(int i = 1;i<=n;i++){
			cin >> a[i];
			s[i] = s[i-1] + a[i];
			max_val = max(max_val,a[i]);
		}
		int ans = -LINF;
		for(int i = 0;i<=n;i++){
			int now = s[i];
			int ctr = 1;
			for(int j = i+1;ctr * 2 <= max_val && j<=n ;j++){
				ctr *= 2;
				now += a[j] / ctr;
			}
			now -= i * k;
			ans = max(ans , now);
		}
		cout << max(ans,0LL) << endl;
	//	debug(ans);
	}

;