Educational Codeforces Round 66 (Rated for Div. 2) (B思维、C枚举、D思维、E经典倍增)

心得

体验较差,一方面是Bwa了若干发,然后看别人代码就很简单

另一方面是D其实是一个简单题,然而碍于前面受挫没做出来

B - Catch Overflow!

维护一个x(0<=x<=(1ll<<32)-1),l(l<=1e5)次操作,操作分三种

①for v 开一个执行v次的循环(1<=v<=100)

②end 终止上一次循环

③add 对x+1

如果x溢出,即x>=(1ll<<32),输出OVERFLOW!!!

否则输出x的值

题解

感觉就是a[cnt]=min(a[cnt-1]*v,INF)这里不大好想叭

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1ll<<32;
const int maxn=1e5+10;
int l,n;
ll a[maxn],cnt,ans,v;
char s[8];
bool ok;
int main()
{
	scanf("%d",&l);
	a[cnt]=1;
	for(int i=1;i<=l;++i)
	{
		scanf("%s",s);
		if(s[0]=='f')
		{
			scanf("%I64d",&v);
			cnt++;
			a[cnt]=min(a[cnt-1]*v,INF);
		}
		else if(s[0]=='e')cnt--;
		else 
		{
			ans+=a[cnt];
			if(ans>=INF)
			{
				ok=1;
				break;
			}
		}
	}
	if(ok)puts("OVERFLOW!!!");
	else printf("%I64d\n",ans);
	return 0;
}

C - Electrification

数轴上n(n<=2e5)个点的位置,第i个位置为ai(1<=ai<=1e9),

你需要选择一个点d,使得分别对这n个点求距离所得序列dis[]中,

dis[]增序第k+1个值最小,输出这个d的位置,多解取其一即可

题解

枚举,取a[i]到a[i+k]这一段的最小,d在其中取,

第k+1个值肯定是到a[i]的距离和到a[i+k]的距离,二者取其大

最小化这两个值的最大值,取中间即可

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2e5+10;
int t,n,k,a[maxn];
int res,ans;
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		res=INF;
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;++i)
		scanf("%d",&a[i]);
		for(int i=1;i+k<=n;++i)
		{
			if(res>a[i+k]-a[i])
			{
				res=a[i+k]-a[i];
				ans=(a[i+k]+a[i])/2;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
} 

D - Array Splitting

有一个序列n(n<=3e5),第i个数为ai(|ai|<=1e6,注意ai可负)

给定一个数k(k<=3e5),要求把序列分成k段的区间,

\sum_{i=1}^{n}a_{i}*f(i)的最大值,f(i)定义为ai这个数最后属于从左到右的第i段

题解

譬如,画这么一个图,把竖着看的高度hi,认为是属于第i段,即fi,

那么这个图,对应[1,1][2,3][4,4][5,5],n=5,k=4的情形,

横着看的话,就是后缀和suf[5]+suf[4]+suf[2]+suf[1]

suf[1]肯定是必取的,剩下k-1个后缀,取最大的k-1个就可以了,

所求和是横着看的最大值,也就是竖着看的最大值

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
typedef long long ll;
int n,k;
ll a[maxn],suf[maxn];
ll ans;
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)
	scanf("%I64d",&a[i]);
	for(int i=n;i>=1;--i)
	suf[i]=suf[i+1]+a[i];
 	ans+=suf[1];//必选1个完整区间 
 	sort(suf+2,suf+n+1);
 	for(int i=n;i>=n-k+2;--i)//再选k-1个最大区间 
 	ans+=suf[i];
 	printf("%I64d\n",ans);
	return 0;
}

E - Minimal Segment Cover

思路来源

https://www.cnblogs.com/wxyww/p/CF1175E.html

心得

用到了倍增lca+贪心区间最小覆盖的思想,每次跳最右

本来写了个用线段树维护下一跳跳到哪里最右的然而T了,所以还是得倍增

这连续好几次都是dp维护最左/最右然后判断能不能成立的题了,还是不熟练啊

题解

dp[i][j]代表从i这个起点起,向右走2^{j}步,能到达的最右端点

dp[i][0]=max(dp[i][0],max(i,dp[i-1][0]),从相邻状态转移

倍增就是裸的了,每次去走不超过的最大步长,最后把那最逼近的一步小的走完

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int lg=20;
//dp[i][j]代表从i起走j步的最右 
int dp[maxn][lg];
int n,m,l,r,mx;
int ask(int l,int r)
{
	int sum=0;
	for(int len=19;len>=0;--len)
	{
		if(dp[l][len]<r)
		{
			l=dp[l][len];
			sum+=(1<<len);
		}
	}
	l=dp[l][0];sum++;
	if(l<r)return -1;
	return sum;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d",&l,&r);
		dp[l][0]=max(dp[l][0],r);
		mx=max(mx,r); 
	}
	for(int i=1;i<maxn;++i)
	dp[i][0]=max(dp[i][0],max(i,dp[i-1][0]));
	for(int len=1;len<=19;++len)
	{
		for(int i=0;i<=mx;++i)
		dp[i][len]=max(dp[i][len],dp[dp[i][len-1]][len-1]);
	}
	while(m--)
	{
		scanf("%d%d",&l,&r);
		printf("%d\n",ask(l,r));
	}
	return 0;
} 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值