CF401

A.Vanya and Cards

CF原题链接

题目大意:

给出n个数ai,满足|ai|x,要求添加若干个满足以上要求的数,使得Σai=0,求添加数字的最小数量(1n,x1000)

解题思路:

直接做做完了。统计一下原本Σai的值d|d|x就是答案。

小代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+5;
int n,x;
int a[N];
int sum;

signed main()
{
	scanf("%lld%lld",&n,&x);
	for (int i=1;i<=n;i++) 
	{
		scanf("%lld",&a[i]);
		sum+=a[i];
	}
	if (sum<0) sum=-sum;
	printf("%lld",(sum+x-1)/x);
	return 0;
}

B.Sereja and Contests

CF原题链接

题目大意:

共有两类数,一类无限制,二类的数的值必须是一类某数的数值+1,这些数一起组成[1,n]

题目中给出n,k,给定k组数据op,num1,num2,若op=1,代表给出的num1为一类数,num2为二类数;若op=2,那么不输入num2num1为一类数。问剩下未给定的数中最多、最少有几个一类数。(1n,k4000)

解题思路:

最多的一类数的个数就是剩下的数的总数,统计一下即可。

最少的一类数一定是连续两个数中存在一个,只需统计剩下的数中连续两个数的个数(这就相当于是二类数的数量),再用最多的一类数的个数减去它就是答案

坏代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e3+5;
int n,k;
bool flag[N];
int ans1,ans2;

signed main()
{
	scanf("%lld%lld",&n,&k);
	for (int i=1;i<=k;i++)
	{
		int op,num1,num2;
		scanf("%lld%lld",&op,&num1);
		if (op==2) flag[num1]=true;
		else
		{
			scanf("%lld",&num2);
			flag[num1]=flag[num2]=true;
		}
	}
	for (int i=1;i<n;i++)
	{
		if (!flag[i]) ans1++;
	}
	ans2=ans1;
	for (int i=1;i+1<n;i++)
	{
		if (flag[i]==false&&flag[i+1]==false)
		{
			ans2--;
			i++;
		}
	}
	printf("%lld %lld",ans2,ans1);
	return 0;
}

C.Team

CF原题链接

题目大意:

给出n,m(1n,m106),要求构造出仅由n0m1组成,且没有2个连续的0、没有3个连续的1的数列。若无法构造,输出1

解题思路:

先考虑无法构造的情况。可以发现,数列中最多有(n+1)×21,最少有n11,若m不在这个范围内,那么一定无法构造成功。

接下来考虑"011"与"01"。若m>n,那么一定是构造011更优,若二者相等,那么构造01更优。若构造完成后有剩余的1,开头特判提前输出即可。

不好的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,m;
string ans;

signed main()
{
	cin>>n>>m;
	if ((n+1)*2<m||m<n-1)
	{
		printf("-1");
		return 0;
	}
	if (m==n*2+2) printf("11"),m-=2;
	if (m==n*2+1) printf("1"),m--;
	while (n<m&&n!=0&&m>0)
	{
		printf("0");
		int cnt1=min(1LL*2,m);
		m-=cnt1;
		while (cnt1--) printf("1");
		n--;
	}
	while (n!=0&&m!=0)
	{
		m--,n--;
		printf("01");
	}
	while (m--) printf("1");
	while (n--) printf("0");
	cout<<ans;
	return 0;
}

D.Roman and Numbers

CF原题链接

题目大意:

n每个数位上的数字可以重新排列成为新的n,要求nm的倍数,求方案数。(1n<1018,1m100)

不允许有前导0!

解题思路:

翻开题解发现是状压DP,果断放弃摆烂

于是复习了一下很久以前学的状压,就着几篇题解终于把这题吃了

注意到n的范围,题解说一眼状压dp

设状态为fi,j,ki为每个数选或不选构成的集合,状压dp必有的东西;j表示当前方案%mjk表示目前是否出现了0,所以f1<<cnt1,0,1就是最后的答案。(cnt表示n的数位位数)

然后好像就没了?

先预处理出n每一位上的数字ai,因为题目不允许有前导0,所以若ai=0,那么一定要从没有0出现过的状态转移过来;如果不等于0的话,那么就从两种情况转移过来。

状态转移方程(aj0版),其中i为状态,j为枚举的数位,k为余数

f[i|(1<<j)][(k*10+a[j])%m][1]+=f[i][k][1]+f[i][k][0];

然后就水灵灵地做出来了?

水灵灵的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5;
int n,m;
int cnt,a[20];
int vis[10];
int f[N][110][2];

signed main()
{
	scanf("%lld%lld",&n,&m);
	while (n)//处理n的数位
	{
		a[cnt++]=n%10;
		n/=10;
	}
	f[0][0][0]=1;
	for (int i=0;i<(1<<cnt);i++)//状态 
	{
		memset(vis,0,sizeof vis);
		for (int j=0;j<cnt;j++)//数位 
		{
			if (i&(1<<j)||vis[a[j]]) continue;//前面的状态出现过或有重复的
			vis[a[j]]=1;
			for (int k=0;k<m;k++)//余数 
			{
				if (a[j])
				{
					f[i|(1<<j)][(k*10+a[j])%m][1]+=f[i][k][0];
					f[i|(1<<j)][(k*10+a[j])%m][1]+=f[i][k][1];
				}
				else f[i|(1<<j)][(k*10+a[j])%m][1]+=f[i][k][1];
			}
		}
	}
	printf("%lld",f[(1<<cnt)-1][0][1]);
	return 0;
}

E题没了,嗨皮恩定

E题被其他比赛拿走啦

posted @   还是沄沄沄  阅读(114)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示