2023.9.12训练

1.K - 比赛 1228 (qingyandark.top)

思维题,读题需要好好审清楚问题。

首先是没理解“编号与自己差值不大于 2”,只需要考虑相邻三个不能重复,每三个一循环,循环的数字可以循环内调整,多个循环的数字排列必须保持一致。

其次是gcd(x,y)=1,就是2,3,5

构成:2 3 5 2 3 5...,2 5 3 2 5 3...,...等等

最后一点就是,n=1的时候需要特判。

思维的分类讨论:
余0: 完整,有6种。

余1:保证余的最小,是2,所以2 3 5、2 5 3,2种情况

余2: 保证余的最小,是2、3,所以是2 3 5、3 2 5两种情况

int n,a[3]={2,5,10};
scanf("%d",&n);
if(n = 1)
puts("2 1");
else
printf("%d %d\n",(n-1)/3*10+a[(n-1)%3],n%3?2:6);

2.B - 比赛 1228 (qingyandark.top)

这道题没有完整思考出解决方案

只思考了需要连续的一段进行计算和,但是应该是在去除这一段后将其左右两段加和,合并成一段--左右两个位置的都是一个属性,这一点没有想到

可以使用vector,然后进行擦除进行操作,也可使用数组。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,a,b;
char s[8050];
int val[8050],tot=0;

void input()
{
	scanf("%d%d%d%s",&n,&a,&b,s);
	int cnt=1;
	for(int i=1;i<n;i ++ )
	{
		if(s[i] != s[i-1])
		{
			val[ ++ tot]=cnt*(s[i-1] == 'A'?a:b);
			cnt=1;
		}
		else
			cnt ++ ;
	}
	val[ ++ tot]=cnt*(s[n-1] == 'A'?a:b);
}
void solve()
{
	int ans[2]={0,0},cur=1;
	while(tot != 0)
	{
		int mx=0,mxp=0; //最大权值及其所在位置
		for(int i=1;i <= tot;i ++)
			if(val[i]>mx)
			{
				mx=val[i];
				mxp=i;
			}
		ans[cur]+=mx; //加入答案
		cur^=1;
		if(mxp == 1) //在首位置,把该位置权值删去,其余权值左移一格即可
		{
			for(int i=1;i<tot;i ++ )
				val[i]=val[i+1];
			tot -- ;
		}
		else if(mxp == tot) //在尾位置,无需处理
		{
			tot -- ;
		}
		else // 在中间,需要将左右两边权值合并,并把右侧权值左移两格
		{
			val[mxp-1]+=val[mxp+1];
			for(int i=mxp;i<tot-1;i++ )
				val[i]=val[i+2];
			tot-=2;
		}
	}
	printf("%d %d\n",ans[0],ans[1]);
}
int main()
{
	input();
	solve();
	return 0;
}

3.C - 比赛 1228 (qingyandark.top)

C题待补,set上自动排序不会写,还需要存一下位置。
如下为C题标程

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;

struct node
{
	int val,pos;
	node(){}
	node(int val,int pos):val(val),pos(pos){}
	bool operator < (const node& a) const // 按题意对权值和下标进行排序
	{
		if(val^a.val)return val>a.val;
		return pos<a.pos;
	}
	bool operator == (const node& a) const
	{
		return val == a.val && pos == a.pos;
	}
};
int n,a,b;
char s[200050];
int val[200050];
set<node> sval; //存储权值及下标
set<int> spos; //维护下标,递增

void input()
 {
	scanf("%d%d%d%s",&n,&a,&b,s);
	 int cnt=1;
	 for(int i=1;i<n;i ++ )
		 {
		 if(s[i] != s[i-1])
			 {
			 val[i-cnt]=cnt*(s[i-1] == 'A'?a:b);
			 sval.insert(node(val[i-cnt],i-cnt));
			 spos.insert(i-cnt);
			 cnt=1;
			 }
		 else
			 cnt ++;
		 }
	 val[n-cnt]=cnt*(s[n-1] == 'A'?a:b);
	 sval.insert(node(val[n-cnt],n-cnt));
	 spos.insert(n-cnt);
}

void solve()
 {
	 int ans[2]={0,0},cur=1;
	 while(!sval.empty())
		 {
		 auto it=sval.begin(); //每次取集合顶部加入答案
		 ans[cur]+=(*it).val;
		 cur^=1;
		 if(spos.size()>1)
			 {
			 auto itt=spos.find((*it).pos);
			 if(itt != spos.begin() && itt != ( -- spos.end())) //如果不是首尾元素
				 {
				 auto lef=itt; lef -- ; //取左侧指针
				 auto rig=itt; rig ++ ; //取右侧指针
				 auto lefval=sval.find(node(val[*lef],*lef)); //返回sval集合找出左侧元素
				
				 auto rigval=sval.find(node(val[*rig],*rig)); //返回sval集合找出右侧元素
				node nd=*lefval;
				  nd.val+=(*rigval).val; //合并左右侧元素
				  val[*lef]+=(*rigval).val;
				  sval.insert(nd); //插入原集合中
				  sval.erase(lefval); //并将原本的两相邻元素删除
				  sval.erase(rigval);
				  spos.erase(rig); //对于spos集合,删除右侧下标即可
				  }
			  spos.erase(itt);
			  }
		  sval.erase(it);
		  }
	  printf("%d %d\n",ans[0],ans[1]);
}
int main()
{
	input();
	solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值