Educational Codeforces Round 92 (Rated for Div. 2)-vp ABCDE

4 篇文章 0 订阅

本次开出了3题 第4题有思路开始写不出来 具体原由可能是因为我的码风比较差 写到后面我都不知道这么写了

1.Problem - A - Codeforces

对于这道题 关键的点在于l≤LCM(x,y)≤r

为了尽可能满足这个式子我们可以发现一点:我们要让y尽可能的小

我们可以知道的一点是 lcm=x*y/gcd(x,y),假如此时lcm<=y的话 我们要求x==gcd(x,y) 即y是x的倍数时 我们可以将此式子维护位 x<=lcm<=y (设x<y)所以此时最小的y是由最小的x决定的:

ymin=2*xmin xmin=l

所以当r>2*l时无解 其他情况输出 l,2*l就好

2:Problem - B - Codeforces

这一题的话 我写了20多分钟 本来是想写dp的 结果写不出来 我就直接暴力了

这道题的本质在于这个z仅有5 我们可以枚举每个点回退的次数(可以发现他最多在一个点有回退)

此时我们考虑为什么在一个点回退最好:

假设我们可以在2不同的点开始回退 那么此时我们可以发现一个 每次回退的话回产生一个值a[i]+a[i-1]对于任意两个点回退的话 起码有一个大一个小或相同 那么我们就可以得知 我们将一个点回退此时全部给另一个(价值大的)就好了 这样保证不会差

但是要注意的是我们不一定要将所有回退此时都用完 所以此时我们就要枚举在一个点的回退次数去最大就好了

#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
int a[maxn], n, m,k,z;
int sum[maxn];
int dp[maxn][6];//dp[i][j]前i次用到j次回退可以获得最大值
void solve()
{
	cin >> n >> k >> z;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];//对于每一位我们都考虑将z全部用来回去
		sum[i] = sum[i - 1] + a[i];
	}
	int ans = 0;
	for (int i = 2; i <=k+1; i++)
	{
		int now = a[1]+sum[i]-sum[1];
		for (int j = 0; j <= z; j++)
		{
			if (2 * j + i - 1 <= k)//代表回退后可以继续走
			{		
				int now1 = now + j * (a[i] + a[i - 1]);
				int left = k - 2 * j - i + 1;
				now1 += sum[i + left] - sum[i];
				ans = max(ans, now1);
			}
			else//代表不能走回去
			{
				int left = k - i + 1;
				int now1 = now;
				for (int j1 = 1; j1 <= left; j1++)
				{
					if (j1 % 2)
						now1 += a[i - 1];
					else
						now1 += a[i];
				}
				ans = max(ans, now1);
			}
		}
	}
	cout << ans<<endl;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--)
		solve();
}

C:这道题我写了大概20分钟 主要是想歪了一点 其实这里的思路比较明显 我们考虑要维护两个数字 这两个数字循环出现 就是类似:2525252525这类数字 当我先的是维护nex[i][j]代表点i后面最近的j的位置在哪里 本来也没事大问题就是写麻烦一点 当我写错了 

然后我就发现其实暴力就好根本不用维护这个东西 就是我们跳出s中我们指定的两个数的序列

代码如下:

#include<iostream>
#include<vector>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
int a[maxn], n, m,k,z;
//维护一个nex[i][j] 代表i位置下一个j(数目)在哪里
pair<int, int>pii[110];
int cnt = 0;
void solve()
{
	string s;
	cin >> s;
	int len = s.length();
	int ans =0;
	for (int i = 1; i <=cnt; i++){
		int len1 = 0;
		int now =1;
		for (int j = 0; j < len; j++)
		{
			if (now == 1 && s[j] == pii[i].first+'0')
			{
				len1++;
				now = 2;
			}
			else if (now == 2 && s[j] == pii[i].second+'0')
			{
				len1++;
				now = 1;
			}
		}
		if (pii[i].first == pii[i].second)
		{
			ans = max(ans, len1);
		}
		else
		{
			if (len1 % 2)
			{
				ans = max(ans, len1 - 1);
			}
			else
			{
				ans = max(ans, len1);
			}
		}
	}
	cout << len - ans <<'\n';
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t = 1;
	for (int i = 0; i <= 9; i++)
	{
		for (int j = 0; j <= 9; j++)
			pii[++cnt].first = i, pii[cnt].second = j;
	}
	cin >> t;
	while (t--)
		solve();
}

D:D题的话 其实贪心比较好想 就是我们假如感刚开始的区间交集不足k的话 我们就贪心的去扩大交集 因为我们此时每次扩大1个交集的代价要么是1 要么是2 所以我们先1 后2 就好了

但我写不出来(写假了 连案例都过不去)

这里参照其他大佬的代码写一份代码 并做出调整 且学习差别 

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
int n, k, l1, r1, l2, r2;

void solve()
{
	cin >> n >> k;
	cin >> l1 >> r1;
	cin >> l2 >> r2;
	if (l1 > l2)
	{
		swap(l1, l2);
		swap(r1, r2);
	}//将小的l设为l1
	
	int cross = 0;
	if (r1 > l2&&l1 < r2)//有交集
	{
		cross = min(r1, r2) - max(l1, l2);
	}
	int now = n * cross;
	k -= now;//代表剩下要维护的交集
	if (k <= 0)
	{
		cout << 0<<endl;
		return;
	}
	//此时还没有成功,我们考虑一下一个一个维护 先处理我们要让其到达相交临界的步数
	int need = l2-r1;
	if (need < 0)
		need = 0;
	//开始维护
	int maxx =max(r1,r2)-min(l1,l2)-cross;//每个区间最多可以维护多少个1步区域
	//cout << need << ' ' << maxx << ' ' << cross << endl;
	int tmp = 1e18;
	 now = 0;//此时的步数
	for (int i = 1; i <= n; i++)
	{
		now += need;
		if (maxx >= k)//代表此时我们可以直接维护成功
		{
			now += k;
			k = 0;
			break;
		}
		else
		{
			now += maxx;
			k -=maxx;
			tmp = min(tmp, now + 2 * k);//假设剩下的都利用2步来维护
		}
	}
	if (k > 0)//即我们所有都利用1步是不可以的
	{
		cout << tmp << endl;
	}
	else
	{
		cout << min(now, tmp) << endl;
	}
}
signed main()
{
	int t;
	cin >>t;
	while (t--)
		solve();
}

E:

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
int gcd(int a, int b)
{
	if (b == 0)
		return a;
	else
		return gcd(b, a%b);
}
void solve()
{
	int m, d, w;
	cin >> m >> d >> w;
	int p = min(m, d);
	d--;
	int k = gcd(w, d);
	w /= k;
	//cout << k << endl;
	int imax = (p - 1) / w;
	int ans = (imax)*p - w * (imax + 1)*(imax) / 2;
	cout << ans << endl;
}
signed main()
{
	int t;
	cin >>t;
	while (t--)
		solve();
}

 

总结:

1.我写的代码是基于全部一起变 这样的话不太好维护 像大佬写的一步一步改变的话任意查找错误

还有就是我没有维护将其变为交叉时的代价 这点非常重要 其次就是我考虑最后两步的次数时是瞎搞的(我也记不得我是咋写的)像大佬写的那样是非常好的(就是维护一个后缀 假如将剩下的所有都变为2步时)这样有效的减小了代码量还有思考量 是一个非常值得学习的写法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值