心得
体验较差,一方面是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段的区间,
求的最大值,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这个起点起,向右走步,能到达的最右端点
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;
}