Codeforces Round #849 (Div. 4) A~G2

原题链接:Dashboard - Codeforces Round #849 (Div. 4) - Codeforces

目录

A. Codeforces Checking

B. Following Directions

C. Prepend and Append

D. Distinct Split

E. Negatives and Positives

F. Range Update Point Query

G1. Teleporters (Easy Version)

G2. Teleporters (Hard Version)


A. Codeforces Checking

给定一个小写字母,判断字符串"Codeforces"里是否有这个字符。

void solve() {
	string s="codeforces";
	char c;
	cin>>c;
	for(char x:s) {
		if(c==x) {
			yes;
			return;
		}
	}
	no;
}

B. Following Directions

在平面直角坐标系给定初始坐标 (0,0) 以及移动方向,判断是否经过或到达 (1,1) 点。

void solve() {
	int n,x=0,y=0;
	string s;
	cin>>n>>s;
	for(char c:s) {
		if(c=='U')y++;
		else if(c=='D')y--;
		else if(c=='R')x++;
		else if(c=='L')x--;
		if(x==1&&y==1) {
			yes;
			return;
		}
	}
	no;
}

C. Prepend and Append

字符串 t 可以在一边加上一个 1 ,另一边加上一个 0 ,最后给定的结果为 s 。求 t 的最短长度。

void solve() {
	int n;
	string s;
	cin>>n>>s;
	int l=0,r=n-1;
	while(l<r) {
		if(s[l]-'0'+s[r]-'0'!=1)break;
		l++,r--;
	}
	cout<<r-l+1<<endl;
}

D. Distinct Split

定义f(x)为字符串 x 中不同字符的数量。

给定一个字符串 s ,将它分成两个非空字符串 a 和 b ,使得 f(a)+f(b) 是可能的最大值,求该最大值。

思路:我们可以枚举前1~i个字符为一组,后面的i+1~n个为一组,模拟一下即可。若一个字符出现次数从1变为0了,f(x)则要减一,若一个字符出现次数从0变为1了,f(x)则要加一,其它时候f(x)都不会改变。

void solve() {
	mem(a,0),mem(b,0);
	int n,sum1=1,sum2=0,ans;
	string s;
	cin>>n>>s;
	a[s[0]]++;
	FOR(1,n-1){
		if(b[s[i]]==0)sum2++;
		b[s[i]]++;
	}
	ans=sum1+sum2;
	FOR(1,n-2){
		if(a[s[i]]==0)sum1++;
		if(b[s[i]]-1==0)sum2--;
		a[s[i]]++,b[s[i]]--;
		ans=max(ans,sum1+sum2);
	}
	cout<<ans<<endl;
}

E. Negatives and Positives

给定一个包含 n 个元素的数组 a ,在执行以下操作任意次后,找到数组可能的最大和:

选择 2 个相邻元素并翻转它们的符号。

换句话说,选择一个索引 i ,使 1≤i≤n−1 ,并分配 a[i]=−a[i] 和 a[i+1]=−a[i+1] 。

思路:若负数的个数为偶数个,那么我们一定能使这些负数全都变为正数。因为对于单个负数,题意中操作的本质,就是让负号往前移一位,或者让负号往后移一位,比如 1 -2 3 这组数据,若要将负号往前移一位,则可以操作第一个数和第二个数,数组就变成了-1 2 3,同理想让负号往后移一位,数组就变成了1 2 -3。并且当两个负号挨着的时候,我们可以让它俩同时变成正号。所以当负数个数为偶数时,我们可以让所有负数挤在一起,然后使他们两两变为正数。若负数个数为奇数个的话,则数组中必然有且只有一个负数,我们让那个绝对值最小的数变为负数即可。

void solve() {
	int n,sum=0,ans=0;
	cin>>n;
	FOR(1,n) {
		cin>>a[i];
		if(a[i]<0)sum++,a[i]=-a[i];
		ans+=a[i];
	}
	sort(a+1,a+n+1);
	if(sum%2)ans-=2*a[1];
	cout<<ans<<endl;
}

F. Range Update Point Query

给定一个数组 a ,你总共需要处理 q 次更新和两种类型的查询:

1-l-r     对于 l≤i≤r 将 a[i]的值更新为a[i]的位数之和。

2-x      输出 a[x] 。

思路:我们可以发现,对于一个数x,我们最多进行三次1操作,它之后就不会再改变了。所以我们可以用一个set来存储操作后仍会改变的数字下标,对于每次1操作我们先用lower_bound函数查找下标大于等于l的、操作后数字仍会改变的下标t,若t>r了,则说明l~r区间内的数不会再改变了。而若t<=r,我们则对这个下标t所对应的数字进行操作,并且若a[t]=f(a[t])了,我们就将下标t从set中删除,代表下标t所对应的数字不会再改变了。

int f(int x) {
	string s=to_string(x);
	int sum=0;
	for(char c:s)sum+=c-'0';
	return sum;
}
void solve() {
	int n,m,sum=0;
	set<int>s;
	cin>>n>>m;
	s.insert(n+1);
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		if(f(a[i])!=a[i])s.insert(i);
	}
	for(int i=1; i<=m; i++) {
		int op;
		cin>>op;
		if(op==2) {
			int x;
			cin>>x;
			cout<<a[x]<<endl;
		} else {
			int l,r;
			cin>>l>>r;
			while(1) {
				auto t=*s.lower_bound(l);
				if(t>r)break;
				a[t]=f(a[t]);
				if(a[t]==f(a[t]))s.erase(t);
				l=t+1;
			}
		}
	}
}

G1. Teleporters (Easy Version)

考虑数轴上的点 ,,(0,1,…,n) 在 ,,1,2,…,n 的每个点上都有一个传送器。在第 i 点,你可以这样做:

  • 向左移动一个单位:花费 1 枚硬币。
  • 向右移动一个单位:花费 1 枚硬币。
  • 在i点使用传送器(如果存在的话):它需要 a[i] 金币。因此,你可以传送到点 0 。一旦你使用了传送器,你就不能再使用了。

你有 c 个硬币,从 0 点开始,最多能使用多少个传送器?

思路:由题意可知使用第i个传送器的花费为a[i]+i个硬币,所以我们将a[i]+i存入一个数组,由小到大排序,然后依次遍历看看m个硬币能使用前几个传送器。 

void solve() {
	int n,m,sum=0;
	cin>>n>>m;
	FOR(1,n)cin>>a[i],a[i]+=i;
	sort(a+1,a+n+1);
	FOR(1,n)if(m-a[i]>=0)sum++,m-=a[i];
	cout<<sum<<endl;
}

G2. Teleporters (Hard Version)

考虑数轴上的点 ,,(0,1,…,n) 在 ,,1,2,…,n 的每个点上都有一个传送器。在第 i 点,你可以这样做:

  • 向左移动一个单位:花费 1 枚硬币。
  • 向右移动一个单位:花费 1 枚硬币。
  • 在i点使用传送器(如果存在的话):它需要 a[i] 金币。因此,你可以传送到点 0 ,或者传送到点n+1。一旦你使用了传送器,你就不能再使用了。

你有 c 个硬币,从 0 点开始,最多能使用多少个传送器?

 思路:对于每个传送器,我们花费的代价有两个:a[i]+i或者a[i]+n-i+1。若不考虑起始点为0的话,只需要将这两个花费取min然后排序即可。但是因为起始点为0,所以我们所选的第一个传送器它的花费必然是a[i]+i,因此我们枚举所有传送器作为第一个传送器的情况,具体实现见代码注释。

void solve() {
	int n,m,ans=0;
	cin>>n>>m;
	FOR(1,n) cin>>a[i],b[i]=min(a[i]+i,a[i]+n-i+1);
	sort(b+1,b+n+1);
	FOR(1,n)s[i]=s[i-1]+b[i];//使用前i个传送器的最小代价 
	FOR(1,n){
		if(a[i]+i>m)continue;//若使用第一个传送器的代价就比m大则说明这次a[i]对应的答案为0 
		int p=min(a[i]+i,a[i]+n-i+1);//p表示a[i]在b数组中对应的数 
		int k=upper_bound(s+1,s+n+1,m-(a[i]+i))-(s+1);//m-a[i]-i为使用了第一个传送器之后的剩的钱,二分查找剩下的钱能使用多少个传送器。 
		//若b[k]>=p则说明我们会将第一次使用传送器的代价算进花费,然而实际上我们已经将第一个使用的传送器的代价减掉了 
		if(b[k]>=p)k=upper_bound(s+1,s+n+1,m+p-(a[i]+i))-(s+1),ans=max(ans,k);//因为第一个传送器花费的实际代价可能不是a[i]-i,所以我们的钱要减少a[i]+i-p,也就是"补差价" 
		else ans=max(ans,k+1);//若b[k]<p则说明前k个里不包含我们的第一个传送器,所以答案是k+1 
	}
	cout<<ans<<endl;
}

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值