Codeforces Round #782 (Div. 2)(A-D)

4 篇文章 0 订阅
2 篇文章 0 订阅

搞心态了,第一题怎么交不了,害

A. Red Versus Blue

题意:给定n,r,b满足条件n=r+b且r>b,输出一个字符串又r个R和b个B,要求该字符串满足条件即连续的r或者b最少。

思路:这道题一开始在那找怎么连续最好,后面发现r>b,那么我们只要用B当作隔板将r分成b+1段即可,且每一段R的个数是r/段数+1或者r/段数(因为出发会向下取整,我们得拿出除法mods剩余的r来一个一个加入到原段中。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,r,b;
		vector<int> v; 
		cin>>n>>r>>b;
		int duan=b+1;
		int less=r%duan;
	    for(int i=1;i<=less;i++){
	    	for(int i=1;i<=r/duan+1;i++)cout<<"R";
	    	cout<<"B";
		}
		for(int i=less+1;i<=duan;i++){
			for(int j=1;j<=r/duan;j++)cout<<"R";
			if(i!=duan)cout<<"B";
		}
		cout<<"\n";
	}
}

B. Bit Flipping

题意:给定一个长度为n的01串,操作次数k,要求对这个01字符串进行k次操作后能得到的最大的值。操作内容为:每次选定字符串中的一个字符,其他字符都异或1,即1变成0,0变成1;

这道题卡了挺久,还被a题搞了心态交不上去,最后的c题又是赛后两分钟写出来(估计不会错)

思路:对于这道题,一开始不知道如何下手,一开始在那模拟半天,后面发现应该整体来看,单总操作次数为奇数时,如果该位时1的话,我们应该分配给他奇数次才能使得他最终还是1,如果该位位0的话,我们应该分配给他偶数次才能使得他最终变成1,又因为要得到最大数,那当然得从前往后枚举了,如果该位为1,我们分配给他1次,该位为0,我们分配给他0次,最终将剩余的次数全部留给最后一个,再对最后一位进行特判即可,操作次数位偶数的话九反过来即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,k;
		cin>>n>>k;
	    string s;
	    vector<int> v,ans;
	    cin>>s;
	    int sum=k;
	    if(k%2==1){
	    	for(int i=0;i<s.size()-1;i++){
        //注意分配1次时要考虑他的总次数是否大于0,否则只能分配0;
	    		if(s[i]=='1'&&sum>0)v.push_back(1),sum--;
	    		else v.push_back(0);

			}
			v.push_back(sum);//将剩余次数都给最后一位
			for(int i=0;i<s.size();i++){
             //当前这一位为0,且操作次数为偶数或者当前这一位为1,操作次数为奇数,则答案是1,否则是0;
				if((s[i]-'0')%2==v[i]%2)ans.push_back(1);
				else ans.push_back(0);
			}
			for(int i=0;i<ans.size();i++)cout<<ans[i];
			cout<<endl; 
			for(int i=0;i<v.size();i++)cout<<v[i]<<" ";
			cout<<endl;
		}
		else {
			for(int i=0;i<s.size()-1;i++){
	    		if(s[i]=='0'&&sum>0)v.push_back(1),sum--;
	    		else v.push_back(0);
			}
			v.push_back(sum);
			for(int i=0;i<s.size();i++){
				if((s[i]-'0')%2!=v[i]%2)ans.push_back(1);
                else ans.push_back(0);
			}
			for(int i=0;i<ans.size();i++)cout<<ans[i];
			cout<<endl; 
			for(int i=0;i<v.size();i++)cout<<v[i]<<" ";
			cout<<endl;
		}
	}
}

C. Line Empire

题意:给定一个长度为n的数组pos,表示位置,开始位置为0(首都),从前往后走到终点且每一个位置都要到达。有两种操作。操作1:首都位置为pos,从pos1到pos2,你能消耗(pos2-pos)*b到达pos2 操作2:首都位置为pos,你能将首都移动到pos1,消耗(pos1-pos)*a;

这道题的话感觉会比b简单。赛后看旁边写的是dp,我也有想过dp,但是好像不满足无后效性遍没有继续尝试。接着,我又看了样例给的提示。发现好像能用前缀o(n)贪心解决掉问题.

我们发现我们最终的首都会在0到posi的某一个位置,于是我们枚举他,表示我们走到终点是首都是posi,那么我们会发现对于posi后面的所有位置posj,他们的消耗一定是(posj-posi)*b;

对于posi前面的位置,我们发现好像不能固定他的路径,他可能突然将首都从0直接搬到posi,又或者从前面的posk搬到posi,这样总消耗就不能确定了,但是我们发现,无论我们怎样将首都搬到posi,消耗一定是posi*a;然后我们就差计算前面这一段进行操作一的消耗了,我们会发现,当我们每次都选择搬首都的情况会使得消耗最少。

比如 0 1 5 6 21 30,0代表最初位置,假如我们最后将首都搬到5的话,对于5前面这一段的消耗,如果我们选择直接从0搬到5,则消耗为(5+5)*b,但是我们从0搬到1再搬到5,消耗为(1+4)*b,

最终消耗的一定会是每次都选择搬为最少消耗。最少消耗为posi*b;

这样,我们就把所有需要的东西都求到了呀,只需要用前缀和维护后面那一段就可以求总的消耗了,枚举完取最小值即为答案;

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2e2+10;
int main(){
	ll t,pos[N]={0},sum[N]={0};
	cin>>t;
	while(t--){
		ll n,a,b;
		cin>>n>>a>>b;
		for(int i=1;i<=n;i++)cin>>pos[i],sum[i]=sum[i-1]+pos[i];
		ll ans=sum[n]*b;//一直都不搬首都的情况
		for(int i=1;i<=n;i++){
			ll tmp1=(sum[n]-sum[i]-pos[i]*(n-i))*b;//后面一段的消耗
			ll tmp2=pos[i]*b;//搬家的消耗
			ll tmp3=pos[i]*a;//前面一段的消耗
			ans=min(ans,tmp1+tmp2+tmp3);
		}
		cout<<ans<<endl;
	}
}

肝不动了,刚刚系统更新完了,交了过了,明天看看dp是个啥情况吧

d题一眼都没看到呜呜呜太菜了

补d题了

D. Reverse Sort Sum

题意:给定一个01串A,可以进行操作f(k,A):将A的前k个数字排序生成新的数组B;

如A{1 0 1 0} f(3,A)={0 1 1 0};最终将所有的b数组的每一位加起来的和得到c数组,输入c数组,要求你输出a数组;

总结:这道题早上看了也没啥思路。看完严格鸽的题解才懂的,还是太妙了,怎么感觉d题总是树状数组呜呜呜,还有线性的解法。

思路:从后往前枚举,第i位,当c数组个数大于等于位置数i时说明他这一位的a数组一定是1,如果小于i,我们可以知道他前面形成的排列已经足够将他的c数组变成i了,只有他为0,才可能在前面排列时不给他那么多的贡献。遍历完这个数后这个数就相当于从我们的世界消失了,于是将他的贡献消除即可。用差分数组来维护即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const ll N=2e5+10;
struct BIT
{
	const static int maxnum=2e5+10;
	int tree[maxnum];
	int lowbit(int x){
		return x&(-x);
	}
	void add(int idx,int x){
		for(int pos=idx;pos<maxnum;pos+=lowbit(pos)){
			tree[pos]+=x;
		}
	}
	int que(int n){
		int ans=0;
		for(int pos=n;pos;pos-=lowbit(pos)){
			ans+=tree[pos];
		}
		return ans;
	}
	int que(int a,int b){
		return que(b)-que(a-1);
	}
	void init(int n){
		for(int i=0;i<=n+2;i++)tree[i]=0;
	}
 } bit;
signed main(){
	int t,c[N],ans[N];
	cin>>t;
	c[0]=0;
	while(t--){
		int n,sum=0;
		cin>>n;
		bit.init(n);
		for(int i=1;i<=n;i++){
			cin>>c[i];
			sum+=c[i];
			bit.add(i,c[i]-c[i-1]);
			ans[i]=0;
		}
		int cnt=sum/n;
		for(int i=n;i>=1;i--){
			int tot=bit.que(i);
			if(tot>=i){
				ans[i]=1;
				bit.add(i-cnt+1,-1);
				bit.add(i+1,1);
				cnt--;
			}
		}
		for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
		cout<<endl;
	}
}

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值