Educational Codeforces Round 91 (Rated for Div. 2) D,E

题目:D. Berserk And Fireball

题意:给定数组a,数组b,要让a变为b,可以连续k个数字花费x全部清除,或者花费y相邻的数字小的消除。

首先可以考虑到 y k y*k 与x的大小关系,决定了我们的策略,是个贪心,先预处理b[0],b[m+1],标记区间,len为一段区间的长度,一段区间内我们分析:
1:len<k,只能一个一个做,花费y,判断一下与端点的大小即可
2:len>=k,如果( y k > = x y*k>=x )建议取第一种方法,由于每个数都不同,我们把(len%k)个数肯定可以全部y花费解决其余x。
否非优先y,如果都小于端点,全部y;否则留一个k长度就行。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=998244353;
const int MAXlen=1e5+10;
long double eps=1e-9;
int a[200100];
int b[200010];
int book[200010]={0};
map<int,int>hh;
int findMAX(int l,int r)
{
    int MAX=0;
    for(int i=l;i<=r;i++)
    {
        MAX=max(a[i],MAX);
    }
    return MAX;
}
int main()
{
    int n,m,i,j,x,k,y;
    scanf("%d %d",&n,&m);
    scanf("%d %d %d",&x,&k,&y);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        hh[a[i]]=i;
    }
    b[0]=-2;
    hh[b[0]]=0;
    b[m+1]=-1;
    hh[b[m+1]]=n+1;
    for(i=1;i<=m;i++)
    {
        scanf("%d",&b[i]);
    }
    ll ans=0;
    for(i=1;i<=m+1;i++)
    {
        int l=hh[b[i-1]],r=hh[b[i]];
        if(r-l==1)
            continue;
        if(r<l)
        {
            printf("-1");
            return 0;
        }
        int ghs=max(a[l],a[r]);
        ll temp=y*k;
        ll len=r-l-1;
        int MAX=findMAX(l+1,r-1);
        if(r-l-1<k)
        {
            if(MAX>a[l]&&MAX>a[r])
            {
                printf("-1");
                return 0;
            }
            else
            {
                ans+=y*len;
            }
            continue;
        }
        else
        {
            if(y*k>=x)
            {
                ans+=(len/k)*x+(len-(len/k)*k)*y;
                continue;
            }
            else
            {
                if(MAX<a[l]||MAX<a[r])
                {
                    ans+=len*y;
                }
                else
                {
                    ans+=(len-k)*y+x;
                }
            }
        }
    }
    printf("%I64d",ans);
    return 0;
}

题目:E. Merging Towers

题意:有m个塔,塔上有圆盘,圆盘从大到小递减排列(自下而上)。
定义了"难度":为将他们全部合并的操作次数。
m-1次询问,每次合并给定的两个塔的圆盘,剩余的”难度“是多少。

可以所谓的操作数,其实也就是找连续的数字在一起的当作一次。
往后合并aa,bb之后,遍历长度较小的那个塔设圆盘为v,如果大的塔里有v-1,v+1的值,那么v进去解决了v+1,v-1的贡献,就连续了。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=998244353;
const int MAXlen=1e5+10;
long double eps=1e-9;
int a[200100];
int cnt=0;
int id[200010];
vector< set<int> >hh(200100);
int main()
{
    int n,m,i,j;
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        hh[a[i]].insert(i);
    }
    for(i=1;i<=n;i=j)
    {
        j=i+1;
        while(j<=n&&a[i]==a[j])
            j++;
        cnt++;
    }
    for(i=1;i<=m;i++)
        id[i]=i;
    printf("%d\n",cnt-1);
    for(int uu=1;uu<m;uu++)
    {
        int aa,bb;
        scanf("%d %d",&aa,&bb);
        set<int>&s1=hh[aa];
        set<int>&s2=hh[bb];
        if(s1.size()<s2.size())
            swap(s1,s2);
        for(auto v:s2)
        {
            cnt-=s1.count(v-1)+s1.count(v+1);
        }
        for(auto v:s2)
        {
            s1.insert(v);
        }
        s2.clear();
        printf("%d\n",cnt-1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43958964/article/details/107372794