Educational Codeforces Round 61 CDEFG题解

C. Painting the Fence

题意:有q个区间,区间范围为1-n,要求q-2个区间所能覆盖的最大的范围。

思路:先把所有区间都加上1,得到总范围sum,然后暴力枚举两个区间,sum减去这两个区间所能得到的值的和答案max一下即可,可以用点为1和点为2的前缀和数组快速求出删除两个区间后的总范围。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
int l[maxn],r[maxn],a[maxn],n,ans,res;
int sum[maxn][4];
int main()
{
	int q;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&l[i],&r[i]);
		a[l[i]]++,a[r[i]+1]--;
	}
	for(int i=1;i<=n;i++)
	{
		a[i]+=a[i-1];
		if(a[i])res++;
		for(int j=0;j<3;j++)
		sum[i][j]=sum[i-1][j];
		if(a[i]==1)sum[i][1]++;
		if(a[i]==2)sum[i][2]++;
	}
	for(int i=1;i<q;i++)
	for(int j=i+1;j<=q;j++)
	{
		int ql=max(l[i],l[j]);
		int qr=min(r[i],r[j]);
		if(ql>qr)
		{
			int t=sum[r[i]][1]-sum[l[i]-1][1];
			t+=sum[r[j]][1]-sum[l[j]-1][1];
			ans=max(ans,res-t);
		}
		else
		{
			int t=sum[qr][2]-sum[ql-1][2];
			int ll=min(l[i],l[j]);
			int rr=max(r[i],r[j]);
			t+=sum[ql][1]-sum[ll-1][1];
			t+=sum[rr][1]-sum[qr][1];
			ans=max(ans,res-t);
		}
	}
	cout<<ans;
}

D. Stressful Training

题意:有n个东西有初始电量ai,每秒耗电bi,持续k秒种,每个东西的电量都不能为负数,你可以每秒选择某一个东西充电ans,求最小的ans。

思路:首先二分ans,接下来如何去check呢?很多人写了O(n)的check,实在是巧妙,我水平垃圾一点就写个nlogn的check吧,用线段树保存每个东西当前电量所能工作的时间的区间最小值,然后k秒每秒给线段树中最小值去充电即可,当线段树区间最小值小于0时就不合法。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;
ll mn[maxn*4],v[maxn*4],a[maxn],b[maxn];
int n,k,tag[maxn*4];
void build(int o,int l,int r)
{
	tag[o]=0;
	if(l==r)
	{
		mn[o]=a[l]/b[l];
		v[o]=a[l]%b[l];
		return;
	}
	int ls=o*2,rs=o*2+1,m=(l+r)/2;
	build(ls,l,m);
	build(rs,m+1,r);
	mn[o]=min(mn[ls],mn[rs]);
}
void qu(int o,int l,int r,ll res)
{
	int ls=o*2,rs=o*2+1,m=(l+r)/2;
	if(l==r)
	{
		mn[o]+=res/b[l];
		v[o]+=res%b[l];
		mn[o]+=v[o]/b[l];
		v[o]%=b[l];
		return;
	}
	if(mn[ls]<=0&&mn[rs]<=0)return;
	if(mn[ls]<mn[rs])qu(ls,l,m,res);
	else qu(rs,m+1,r,res);
	mn[o]=min(mn[ls],mn[rs]);
}
int ok(ll res)
{
	build(1,1,n);
	for(int i=1;i<k;i++)
	{
		qu(1,1,n,res);
		if(mn[1]-i+1<=0)return 0;
	}
	return 1;
}
int main()
{
	ll l=0,r=0,m;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&b[i]);
		r=max(r,b[i]*k-a[i]);
	}
	if(!ok(r))puts("-1"),exit(0);
	while(l<r)
	{
		m=(l+r)/2;
		if(ok(m))r=m;
		else l=m+1;
	}
	printf("%lld\n",l);
}

E. Knapsack

题意:有cnti个i,要求这些数所能组成的一个最大的数ans,ans<=w。

思路:暴搜,我们发现8个1可以用1个8代替,因此每个数从最大数量递减搜7次(我搜3次也能过)即可,复杂度7^8。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll w,a[10],ans;
void dfs(int x,ll res)
{
    if(!x)
    {
        ans=max(ans,res);
        return;
    }
    for(ll i=3,num=min(a[x],(w-res)/x);i;i--,num--)
    dfs(x-1,res+max(0ll,num*x));
}
int main()
{
    cin>>w;
    for(int i=1;i<=8;i++)cin>>a[i];
    dfs(8,0);
    cout<<ans;
}

F. Clear the String

题意:给你一个字符串,每次可以把连续相同的字串删除,求最小次数把字符串删完。

思路:区间dp,设d[ i ][ j ]为 [ i , j ]的字串删除完的最小次数,很显然d[ i ][ j ]=min(d[ i ][ j ],d[ i ][ k ]+d[ k+1 ][ j ]+s[i]==s[k+1] || s[i]==s[j] || s[k]==s[k+1] || s[k]==s[j]?-1:0),如果s[i]==s[j],d[ i ][ j ]=min(d[ i ][ j ],d[i+1][ j-1 ]+1),还有两个很显然的转移方程就不写了。

#include<bits/stdc++.h>
using namespace std;
int d[501][501],n;
char a[501];
int dfs(int l,int r)
{
	if(l>r)return 0;
	if(l==r)return 1;
	if(d[l][r])return d[l][r];
	int res=1e9;
	for(int i=l;i<r;i++)
	{
		int tmp=dfs(l,i)+dfs(i+1,r);
		if(a[l]==a[i+1]||a[l]==a[r]||a[i]==a[i+1]||a[i]==a[r])
		tmp--;
		res=min(res,tmp);
	}
	
	if(a[l]==a[r])
	res=min(res,dfs(l+1,r-1)+1);
	if(a[l]==a[l+1])
	res=min(res,dfs(l+1,r));
	if(a[r]==a[r-1])
	res=min(res,dfs(l,r-1));
	return d[l][r]=res;
}
int main()
{
	scanf("%d%s",&n,a+1);
	printf("%d\n",dfs(1,n));
}

G. Greedy Subsequences

题意:给你一个长度为n的序列,让你求所有长度为k的连续子序列的"最长递增长度"(这个递增指的是题目要求的递增,不同于常识的那个)。

思路:对于每个数ai,用单调栈求出右边第一个比它大的数aj,然后连边aj-->ai,然后将序列转换成树结构,接下来对这棵树跑dfs序,然后用最大值线段树保存每个节点的深度,假设求出了[ 1 k ]的答案mx[1],接下来我们右边移一下,先将第一个数的值在线段树中减去无穷大,然后把第k+1个数在树结构中的子树节点全部+1,就可以了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int mx[maxn*4],tag[maxn*4],cnt,sz[maxn],a[maxn],id[maxn];
vector<int>G[maxn];
stack<int>s;
void dfs(int u)
{
    sz[u]=1;
    id[u]=++cnt;
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        dfs(v);
        sz[u]+=sz[v];
    }
}
void pushdown(int o,int ls,int rs)
{
    if(tag[o]!=0)
    {
        tag[ls]+=tag[o],tag[rs]+=tag[o];
        mx[ls]+=tag[o],mx[rs]+=tag[o];
        tag[o]=0;
    }
}
void up(int o,int l,int r,int ql,int qr,int v)
{
    int m=(l+r)/2,ls=o*2,rs=o*2+1;
    if(l!=r)
    pushdown(o,ls,rs);
    if(l>=ql&&r<=qr)
    {
        mx[o]+=v;
        tag[o]+=v;
        return;
    }
    if(ql<=m)up(ls,l,m,ql,qr,v);
    if(qr>m)up(rs,m+1,r,ql,qr,v);
    mx[o]=max(mx[ls],mx[rs]);
}
int main()
{
    int n,k,ans=0;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    for(int i=n;i;i--)
    {
        while(!s.empty()&&a[i]>=a[s.top()])s.pop();
        if(!s.empty())
        G[s.top()].push_back(i);
        s.push(i);
    }
    for(int i=n;i;i--)
    if(!id[i])dfs(i);
    for(int i=1;i<=k;i++)
    up(1,1,n,id[i],id[i]+sz[i]-1,1);
    printf("%d",mx[1]);
    for(int i=k+1;i<=n;i++)
    {
        up(1,1,n,id[i-k],id[i-k],-10000000);
        up(1,1,n,id[i],id[i]+sz[i]-1,1);
        printf(" %d",mx[1]);
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长沙橘子猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值