Educational Codeforces Round 76 (Rated for Div. 2) DE

D. Yet Another Monster Killing Problem

题意:有m个怪兽,power为a[i] ,有n个hero,他们都有自己的power和每天最多能处理的怪物数s[i] , 在每个英雄都可以使用多次的条件下问处理完这些怪兽最少需要多少天。m个怪兽只能按顺序处理,一天之能派出1位英雄,当下一个怪兽的power大于派出的英雄时,或者英雄今天处理的怪兽数达到s[i],今天英雄就不再继续处理怪物。如果不能处理完输出 -1。

分析:

  • 比赛时看到这道题时先想到的是二分天数,发现check()不好写。分析因为是按顺序处理的,所以要处理总天数最小,每一天处理的怪物一定要最多,所以二分每一天的最多处理的怪物数,当时我写的是 (O(nmlogn)) ,但当时算成了(O(nlogn)) ,果然被Hack了。
  • 赛后听到ST表,便朝着这方向分析,想着ST表(O(nlogn)) 建表,O(1)查询,想了想是不就可以O(1)计算每个区间的最大值,那么所有问题就变成了已知一段长度为len的区间的最大值maxx,寻找有没有英雄的力量 p>maxx ,并且s>len 。计算了一下要O(1)才行,但怎么想都要遍历hero,看题解。
  • 题解很妙,用h[i]表示长度为i的区间所能处理的最大值。更新出h[i]后,只需要遍历怪物扩大区间,判断当前区间最大值是不是超过h[len] 即当前区间长度时所能处理的最大值。如果大于那么前面的区间一定是一个所能处理的最大的区间段,天数++,更新区间左标记。
  • 总结:这道题要有两个要考虑的值p,s。如果只有一个那太简单了。所以我们可以将两个值建立关系,然后只着重考虑一个值(建立的关系是知道该值后,另一个的最优值直接就知道了)。这样就可以线性查询了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MA=2e5+5;
 
ll a[MA],h[MA];
 
int main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%lld",&a[i]);
            h[i]=0;
        }
        scanf("%d",&m);
        for(int i=1;i<=m;++i){
            ll x,y;
            scanf("%lld%lld",&x,&y);
            h[y]=max(h[y],x);
        }
        for(int i=n-1;i>=0;--i)h[i]=max(h[i],h[i+1]);
 
        int l=0,ans=1;
        ll maxx=0;
        for(int i=1;i<=n;++i){
            maxx=max(maxx,a[i]);
            if(h[1]<maxx){
                ans=-1;
                break;
            }
            else if(maxx>h[i-l]){
                maxx=0;
                l=i-1;
                ans++;
                i--;
            }
        }
        printf("%d
",ans);
    }
    return 0;
}






E. The Contest

题意: 给出三个序列的值( 1到 n),移动三个序列中的一些值,使得第一个序列是1 ~ n的一个前缀,第三个序列为1~ 2的一个后缀,第二个序列是其他的值。问移动次数的最小值。

分析:

  • 先放个例子。

  • 我们试着写出每个值所属的序列:13123 (这就是1,2,3,4,5每个值所在的组)。

  • 问题就转变为给你一段只有1,2,3三个值的序列,通过改变最少次数的值(只能改为1,2,3之一),使序列某前缀全为1,某后缀全为3,其他部分为2。

  • 用 $dp[i] [j] $ 表示将第 i 位的值改为 j (即 i 在第 j 组)时的前i位满足题意的改动次数。转移见代码注释。

  • 题解太妙了,也是先转成上面的序列,然后将最小化需要改动位置数量的问题转成最大化不需要改动位置数量的问题。也就是求LIS。
    最后答案就是 N-LIS

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MA=2e5+5;

int a[5],vis[MA],dp[MA][5];

int main()
{
    int n=0;
    scanf("%d%d%d",&a[0],&a[1],&a[2]);
    for(int k=0;k<=2;++k){
        n=a[k];
        int x;
        for(int i=0;i<n;++i){
            scanf("%d",&x);
            vis[x]=k+1;        //构造新序列
        }
    }
    n=a[0]+a[1]+a[2];
    for(int i=1;i<=n;++i){
        if(vis[i]==1){  
            dp[i][1]=dp[i-1][1]; 
            dp[i][2]=min(dp[i-1][2]+1,dp[i-1][1]+1);   
            dp[i][3]=min(dp[i-1][1]+1,min(dp[i-1][2]+1,dp[i-1][3]+1));
        }
        else if(vis[i]==2){
            dp[i][1]=dp[i-1][1]+1;
            dp[i][2]=min(dp[i-1][1],dp[i-1][2]);
            dp[i][3]=min(dp[i-1][1]+1,min(dp[i-1][2]+1,dp[i-1][3]+1));
        }
        else if(vis[i]==3){
            dp[i][1]=dp[i-1][1]+1;
            dp[i][2]=min(dp[i-1][1]+1,dp[i-1][2]+1);
            dp[i][3]=min(dp[i-1][1],min(dp[i-1][2],dp[i-1][3]));
        }
    }
    printf("%d
",min(dp[n][1],min(dp[n][2],dp[n][3])));
    return 0;
}

原文地址:https://www.cnblogs.com/A-sc/p/11868640.html