Codeforces Round #778 (Div. 1 + Div. 2) - B, C

比赛链接:https://codeforces.com/contest/1654


C. Alice and the Cake

题意
有一块蛋糕经过下面 n − 1 n-1 n1 次操作被切成了 n n n 块:

  • 选择一块重量 w ≥ 2 w\ge 2 w2 的蛋糕,将其切成两块重量分别为 ⌊ w 2 ⌋ \lfloor\frac{w}{2}\rfloor 2w ⌈ w 2 ⌉ \lceil\frac{w}{2}\rceil 2w 的小蛋糕。

现在给定最终的 n n n 块小蛋糕,判断其是否能由上面的操作得到?
1 ≤ n ≤ 2 ⋅ 1 0 5 ,   1 ≤ a i ≤ 1 0 9 1 \le n \le 2 \cdot 10^5, \ 1 \le a_i \le 10^9 1n2105, 1ai109

思路
一开始一直在想,到底怎么合并,依次哪两个合并能把所有的合并成一个。。
想了几种策略之后发现都不行,被卡住。。
后面又想,它反正能合并成一个,是由一个切出来的,那么不妨就从最初始的一个往后分,如果分出的一块在数列中出现过,那就把数列中的这个消掉,最后判断数列中的所有数是否都消掉就行了。

Code

#include<bits/stdc++.h>
using namespace std;

#define Ios ios::sync_with_stdio(false),cin.tie(0)

const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N];

signed main(){
	Ios;
	cin >> T;
	while(T--)
	{
		mp.clear();
		
		cin >> n;
		m = 0;
		for(int i=1;i<=n;i++){
			cin >> a[i];
			m += a[i];
			mp[a[i]]++;
		}
		
		stack<int> stk;
		stk.push(m);
		
		int flag = 0;
		while(n)
		{
			int x = stk.top(); stk.pop();
			if(mp[x]) mp[x]--, n--;
			else
			{
				int t1, t2;
				if(x % 2 == 0) t1 = t2 = x/2;
				else t1 = x/2, t2 = x/2+1;
				
				if(!t1){
					flag = 1;
					break;
				}
				
				stk.push(t1);
				stk.push(t2);
			}
		}
		if(flag) cout << "NO\n";
		else cout << "YES\n";
	}
	
	return 0;
}

经验
既然是思维题,那就不能从正常的思路去想,及时的转换思路很关键!从不寻常的思路去考虑!


B. Prefix Removals

题意
给定一个长度为 n 的小写字母串,输出执行过下面的操作后剩下的字符串:

  • 选择最长的一个满足后面要求的前缀,该前缀满足在串中的其他地方也要作为子串出现(可以与前缀串有交叉);
  • 在字符串中删除该前缀串;
  • 重复以上过程,直到找不到满足的前缀串。

n ≤ 2 ⋅ 1 0 5 n \le 2 \cdot 10^5 n2105

思路

6
abcabdc
a
bbbbbbbbbb
codeforces
cffcfccffccfcffcfccfcffccffcfccf
zyzyzwxxyyxxyyzzyzzxxwzxwywxwzxxyzzw

abdc
a
b
deforces
cf
xyzzw

根据样例发现,没必要每次找最长的前缀,只需要看首个字符就行了:

  • 如果这个字符在后面的位置出现过,那么这个字符就可以删去;
  • 否则输出剩下的串。

字符串长度要求 O(n) 的复杂度,那么标记出每个字符最后一次出现的位置就行了,每次判断其出现的位置是否在当前位置之后。

Code

#include<bits/stdc++.h>
using namespace std;

#define Ios ios::sync_with_stdio(false),cin.tie(0)

const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N], p[N];

signed main(){
	Ios;
	cin >> T;
	while(T--)
	{
		for(int i=0;i<26;i++) p[i] = 0;
		
		string s; cin >> s;
		for(int i=0;i<s.size();i++)
		{
			p[s[i]-'a'] = i;
		}
		
		int st = 0;
		for(int i=0;i<s.size();i++)
		{
			if(p[s[i]-'a'] <= i){
				st = i;
				break;
			}
		}
		for(int i=st;i<s.size();i++) cout << s[i];
		cout << endl;
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值