Codeforces Round 957 (Div. 3)

前言

        这场有能力 AK 的,可惜 F 题debug 的时候浪费了太多时间。

        Standings:721

        本次比赛结束后上蓝名,记录一下。(虽然是本就应该的hhh)

        题目链接:Dashboard - Codeforces Round 957 (Div. 3) - Codeforces

A. Only Pluses

        显然,每次操作都让最小的那个数加一就能得到最大乘积。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int T,n,a[5];

int main()
{
	scanf("%d",&T);
	while (T --)
	{
		for (int i = 1;i <= 3;++ i) scanf("%d",&a[i]);
		for (int i = 1;i <= 5;++ i)
		{
			sort(a + 1,a + 4);
			++ a[1];
		}
		printf("%d\n",a[1] * a[2] * a[3]);
	}
	return 0;
}

B. Angry Monk

        贪心,类似于合并果子,排序后每次合并最小的即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define N 200005

int T,n,k,a[N];
long long ans;

int main()
{
	scanf("%d",&T);
	while (T --)
	{
		scanf("%d%d",&n,&k),ans = 0ll;
		for (int i = 1;i <= k;++ i) scanf("%d",&a[i]);
		sort(a + 1,a + k + 1);
		for (int i = 1;i < k;++ i) ans += 1ll * (a[i] + a[i] - 1);
		printf("%lld\n",ans);
	}
	return 0;
}

C. Gorilla and Permutation

        构造题,分情况讨论即可。

#include<cstdio>
#include<cstring>
using namespace std;

#define N 200005

int T,n,m,k,cnt,a[N];

int main()
{
	scanf("%d",&T);
	while (T --)
	{
		scanf("%d%d%d",&n,&m,&k),cnt = 0;
		if(k > m)
		{
			for (int i = n;i >= m + 1;-- i) a[++ cnt] = i;
			for (int i = 1;i <= m;++ i) a[++ cnt] = i;
		}
		else if(k == m)
		{
			for (int i = n;i >= m;-- i) a[++ cnt] = i;
			for (int i = 1;i <= m - 1;++ i) a[++ cnt] = i;
		}
		else
		{
			for (int i = n;i >= k;-- i) a[++ cnt] = i;
			for (int i = 1;i <= k - 1;++ i) a[++ cnt] = i;
		}
		for (int i = 1;i <= n;++ i) printf("%d ",a[i]);
		printf("\n");
	}
	return 0;
}

D. Test of Love

        简单DP。

#include<cstdio>
#include<cstring>
using namespace std;

#define N 200005

int T,n,m,k,a[N],f[N];

int min(int x,int y) { return x < y ? x : y ; }

int max(int x,int y) { return x > y ? x : y ; }

int main()
{
	scanf("%d",&T);
	while (T --)
	{
		scanf("%d%d%d",&n,&m,&k),f[0] = a[0] = a[n + 1] = 0,f[n + 1] = 0x3f3f3f3f;
		for (int i = 1;i <= n;++ i)
		{
			f[i] = 0x3f3f3f3f;
			char s;
			scanf(" %c",&s);
			if(s == 'L') a[i] = 0;
			if(s == 'W') a[i] = 1;
			if(s == 'C') a[i] = 2;
		}
		for (int i = 1;i <= n + 1;++ i)
		{
			if(a[i] == 2) continue;
			if(a[i - 1] == 1) f[i] = min(f[i],f[i - 1]);
			for (int j = max(i - m,0);j <= i - 1;++ j)
				if(!a[j])
					f[i] = min(f[i],f[j]);
			f[i] += (a[i] == 1);
		}
		if(f[n + 1] <= k) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

E. Novice's Mistake

        思维题。

        根据题目范围,n * a 不会超过1000000,于是 n * a - b 不会超过 999999,那么 s 在删去 b 位之后一定小于等于 6 位数。记 len 表示一个 n 的长度,则:a * len - b <= 6

        直接枚举 a 和 b 时间复杂度太大,我们需要换个枚举方式。

        记 k 表示 s 删除了 b 位之后的长度,满足 a * len - b = k <= 6,则 b = a * len - k 。

        对于每个 n ,我们只需枚举 a 和 k 就可以得到 b,再判断是否合法即可。

#include<cstdio>
#include<cstring>
using namespace std;

#define N 10005

int T,n,len,s[5],t[5],cnt,ansa[N],ansb[N];

void swap()
{
	for (int i = 1;i <= len;++ i) t[i] = s[len - i + 1];
	for (int i = 1;i <= len;++ i) s[i] = t[i];
	return;
}

int main()
{
	scanf("%d",&T);
	while (T --)
	{
		scanf("%d",&n),len = cnt = 0;
		int tmp = n;
		while (tmp) s[++ len] = tmp % 10,tmp /= 10;
		swap();
		for (int a = 1;a <= 10000;++ a)
		{
			if(a * len > 10006) break;
			for (int k = 1;k <= 6;++ k)
			{
				int b = a * len - k;
				if(b < 1) break;
				if(b > 10000) continue;
				tmp = 0;
				for (int i = 1;i <= k;++ i)
				{
					int now = i % len;
					if(!now) now = len;
					tmp = tmp * 10 + s[now];
				}
				if(tmp == a * n - b) ansa[++ cnt] = a,ansb[cnt] = b;
			}
		}
		printf("%d\n",cnt);
		for (int i = 1;i <= cnt;++ i) printf("%d %d\n",ansa[i],ansb[i]);
	}
	return 0;
}

F. Valuable Cards

        贪心。

        从左到右进行分组,贪心可知一定要尽可能让当前组里面数字的数目更多,于是在加入每个数的时候标记组里面的数可以产生的所有 x 的因数,要是已经能产生 x 了,那当前这个数就应该放在下一个组的第一个位置。

        比赛的时候程序出 bug 了,注释掉后面的代码,前面输出的值竟然不一样,固然是因为某些地方写挂了,但这让调试十分困难,浪费了大量时间导致没有开 G 题。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define N 100005

int T,n,x,a[N],yin[N],vis[N],cnt,tot;

int main()
{
	scanf("%d",&T);
	int tt = 0;
	while (T --)
	{
		++ tt;
		scanf("%d%d",&n,&x),cnt = tot = 0;
		for (int i = 1;i <= n;++ i) scanf("%d",&a[i]);
		for (int i = 2;i * i <= x;++ i)
			if(!(x % i))
			{
				yin[++ cnt] = i;
				vis[i] = vis[x / i] = 0;
				if(i * i != x) yin[++ cnt] = x / i;
			}
		sort(yin + 1,yin + cnt + 1);
		++ tot;
		if(!(x % a[1])) vis[a[1]] = tot;
		for (int i = 2;i <= n;++ i)
		{
			if(x % a[i] || a[i] == 1) continue;
			if(vis[x / a[i]] == tot)
			{
				++ tot;
				vis[a[i]] = tot;
				continue;
			}
			for (int j = cnt; j ;-- j)
				if(vis[yin[j]] == tot && (long long)yin[j] * a[i] < (long long)x)
					vis[yin[j] * a[i]] = tot;
			vis[a[i]] = tot;
		}
		printf("%d\n",tot);
	}
	return 0;
}

G. Ultra-Meow

        数学计数题,由于太久没做出来这种题,在没看题解的情况下做出来还是开心的。

        我们考虑枚举集合内的元素个数 k ,计算每个 k 的总贡献。首先不难发现,集合大小为 k 的某个集合,它单个的贡献范围是 [k+1,2k+1] 。因此对于每个 k ,我们枚举可能的贡献 x ,算出贡献为 x 的集合数目,再计算总贡献。

        当贡献为 x 时:

        1. 因为 x 是集合外的第 k + 1 个数字,因此在 x 之前(小于 x 的)一定有 x - (k+1) 个数字存在于集合内,这部分的方案数为 C_{min(x - 1,n)}^{x - (k+1)} ,和 n 取 min 是因为有可能 n 比 x - 1 小,这样可选择的数字就只有 n 个而不是 x - 1 个。

        2. 满足了 “1.” 中的条件后,若集合在 x 之后(大于 x 的)还有数字,那就随便选了,这部分的方案数是 C_{n - x}^{2k + 1 - x}

        根据乘法原理,总方案数(集合数目)就是 C_{min(x - 1,n)}^{x - (k + 1)} * C_{n - x}^{2k + 1 - x} 。

        很奇怪的是好多人用 c++ 11 / 14 / 17 都 TLE 了,c++ 20 就能过,也许是 c++ 20 快点儿?

#include<cstdio>
#include<cstring>
using namespace std;

#define M 1000000007
#define N 5005

int T,n;
long long c[N][N],ans;

int min(int x,int y) { return x < y ? x : y ; }

long long ksm(long long x,long long p)
{
	long long tmp = 1ll;
	while (p)
	{
		if(p & 1) tmp = tmp * x % M;
		x = x * x % M;
		p >>= 1;
	}
	return tmp;
}

void pre()
{
	for (int i = 1;i <= 5000;++ i)
	{
		c[i][0] = c[i][i] = 1ll;
		for (int j = 1;i - j + 1 > j;++ j)
			c[i][j] = c[i][i - j] = c[i][j - 1] * 1ll * (i - j + 1) % M * ksm(1ll * j,M - 2ll) % M;
	}
	return;
}

int main()
{
	pre();
	scanf("%d",&T);
	while (T --)
	{
		scanf("%d",&n),ans = 1ll;
		for (int k = 1;k <= n;++ k)
			for (int x = k + 1;x <= 2 * k + 1;++ x)
			{
				int up1 = x - (k + 1);
				int low1 = min(x - 1,n);
				int up2 = 2 * k - x + 1;
				int low2 = n - x;
				int c1 = c[low1][up1];
				int c2 = (!up2) ? 1 : ((low2 < up2) ? 0 : c[low2][up2]);
				ans = (ans + 1ll * x * c1 % M * c2 % M) % M;
			}
		printf("%lld\n",ans);
	}
	return 0;
}

总结

        感觉这场比赛打的还是挺顺的,除了最后调试那里出了点 bug ,后来发现可能是多组数据初始化的时候没清干净导致的神奇问题(虽然理论上是没有问题的,但是既然出 bug 了那以后就要注意)。感觉自己的思维在慢慢提升,争取能尽快在比赛时突破 6 题,早日 AK 一次!

  • 43
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值