Codeforces Round #625 (Div. 2) Mar/01/2020 21:15UTC+8

比赛链接 https://codeforces.com/contest/1321
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197

A. Contest for Robots

在这里插入图片描述
题意:设置每道题的分数让robo赢

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10,inf=2e9;

int n;
int a[maxn],b[maxn];

int main()
{
    cin>>n;
    for(int i=1;i<=n;++i) cin>>a[i];
    for(int i=1;i<=n;++i) cin>>b[i];

    int cnt1=0,cnt2=0;
    for(int i=1;i<=n;++i)
        if(a[i]==1&&b[i]==0) cnt1++;
        else if(a[i]==0&&b[i]==1) cnt2++;

    if(cnt1==0)
    {
        puts("-1");
        return 0;
    }
    int ans;
    if(cnt2%cnt1==0)
        ans=cnt2/cnt1+1;
    else
        ans=ceil(cnt2*1.0/cnt1);
    cout<<ans<<"\n";
    return 0;
}

B. Journey Planning

在这里插入图片描述
题意:求满足 c i + 1 − c i = b c i + 1 − b c i c_{i+1}-c_{i}=b_{c_{i+1}}-b_{c_i} ci+1ci=bci+1bci的最大和

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;

ll n,a[maxn];
map<int,ll> m;

int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;++i)
		scanf("%lld",&a[i]);	
	for(int i=1;i<=n;++i)
		m[a[i]-i]+=a[i];	
	ll ans=0;
	for(auto i :m)
		ans=max(ans,i.second);
	printf("%lld\n",ans);	
	return 0;
}

C. Remove Adjacent

在这里插入图片描述
题意:给定一个字符串 s 。对于一个字符 c ,如果相邻的字符比它小 1 ,那么就是可以移除的。问最多能够移除 s 中多少字符。 ( 1 ≤ ∣ s ∣ ≤ 100 ) (1\le|s|\le 100) 1s100

思路:从 z 开始往 a 那边删。

  • 方法一:每次先对字符串去重,把相同的叠在一起,然后删去。
  • 方法二:每次删去一个字符之后,就重新从 z 开始删字符
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10,inf=2e9;
int n;
string s;
int counts[100];

int main()
{
    cin>>n>>s;
    int ans=0;
    memset(counts,0,sizeof(counts));
    vector<int> num(n,1);
    for(int i=26;i>=1;--i)
    {
        n=s.size();
        string tmp=s.substr(0,1);
        int cnt=-1;
        counts[++cnt]=num[0];
        for(int i=1;i<n;++i)
            if(s[i]==s[i-1]) counts[cnt]+=num[i];
            else counts[++cnt]=num[i],tmp+=s[i];

        num.clear();
        char c='a'+i-1;
        for(int j=cnt;j>=0;--j)
        {
            if(tmp[j]==c)
            {
                if(j+1<=cnt&&tmp[j+1]==c-1)
                    ans+=counts[j],tmp.erase(tmp.begin()+j),counts[j]=0;
                else if(j-1>=0&&tmp[j-1]==c-1)
                    ans+=counts[j],tmp.erase(tmp.begin()+j),counts[j]=0;
            }
        }
        for(int j=0;j<=cnt;++j)
            if(counts[j]!=0)
                num.push_back(counts[j]);
        s=tmp;
    }
    cout<<ans<<"\n";
    return 0;
}

D. Navigation System

在这里插入图片描述
题意:给定一张图,一个指定的路径,人会按照路径走。导航每次都会在一个新的点重新定位最短的路径。求导航最少导航几次,和导航最多导航几次

思路

  • 到达一个点的时候判断一下。如果当前边不在最短路上,mi++,mx++
  • 如果当前边在最短路上,且存在两条最短路,mx++

实现:反向建图,从汇点开始跑dijstra。

  • 假设从 u 走到 v。如果说dis[u]!=dis[v]+1,说明了边(u,v)并不在最短路上面,因此mi++,mx++
  • 相反,如果说dis[u]=dis[v]+1,那么说明边(u,v)在最短路上面。此时找是否有两条以上的最短路。如果有mx++
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10,inf=1e9;

int n,m;
vector<int> g1[maxn],g2[maxn];
int k,p[maxn],dis[maxn],visit[maxn];

void dijstra()
{
    for(int i=1;i<=n;++i) dis[i]=inf;
    dis[p[k]]=0;
    priority_queue<pair<int,int> > pq;
    pq.push({0,p[k]});
    while(!pq.empty())
    {
        int u=pq.top().second;
        pq.pop();
        if(visit[u]) continue;
        visit[u]=1;
        for(auto v : g2[u])
        {
            if(dis[v]>dis[u]+1)
            {
                dis[v]=dis[u]+1;
                pq.push({-dis[v],v});
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        g1[u].push_back(v);
        g2[v].push_back(u);
    }
    scanf("%d",&k);
    for(int i=1;i<=k;++i) scanf("%d",&p[i]);
    dijstra();
    int mi=0,mx=0;
    for(int i=1;i<k;++i)
    {
        int u=p[i],v=p[i+1];
        if(dis[u]!=dis[v]+1)
            mi++,mx++;
        else
        {
            for(auto t : g1[u])
                if(dis[u]==dis[t]+1&&t!=v)
                {
                    mx++;
                    break;
                }
        }
    }
    printf("%d %d\n",mi,mx);
    return 0;
}

E. World of Darkraft: Battle for Azathoth

在这里插入图片描述
题意:n 件武器,第 i 件武器的攻击力为 a i a_i ai,购买需要花费 c a i ca_i cai。m件防具,第 i 件防具的防御力为 b i b_i bi,购买需要花费 c b i cb_i cbi。有 p 个怪兽,怪兽的防御力为 x i x_i xi,攻击力为 y i y_i yi,杀死怪物能够得到的金币为 z i z_i zi。当武器的攻击力大于怪兽的防御力,并且防具的防御力大于怪兽的攻击力时,就可以杀死怪物,获得利益 z z z 。问至少购买一件武器和一件防具后,能够获得的最大利益是多少。

思路:首先可以明确只需要买一件武器和一件防具。然后,按照最暴力的方法想。第一维枚举武器,第二维枚举防具,第三维枚举怪兽,复杂度 O ( n 3 ) O(n^3) O(n3)

  • 怎样优化呢?我们让武器按攻击力从小到大排序。对于怪兽的防御力 x,那么所有大于防御力的武器,都可以杀死这只怪物。也就是这只怪物对这些武器都有贡献,那么就可以想到用线段树来更新贡献,维护利益的最大值。
  • 将武器按攻击力从小到大排序,然后将花费更新到线段树上。枚举防具,对于攻击力(y)小于当前防具防御值的怪兽,对 [pos,n]的武器都存在贡献。更新后得到利益的最大值,再减去防具的花费,就是当前的答案。
  • 可以发现这样的复杂度是 O ( m + p l o g n ) O(m+plogn) O(m+plogn)
#include <bits/stdc++.h>
#define fi first
#define se second
#define ls (rt<<1)
#define rs (rt<<1|1)
#define ll long long
using namespace std;
const int maxn=2e5+10,inf=1e9;

int n,m,p;
pair<int,int> a[maxn],b[maxn];

struct Monster
{
    int x,y,z;
    bool operator<(const Monster & b) const
    {
        return y<b.y;
    }
}mo[maxn];

ll st[maxn<<2],lazy[maxn<<2];

void pushUp(int rt)
{
    st[rt]=max(st[ls],st[rs]);
}

void pushDown(int rt)
{
    if(lazy[rt])
    {
        st[ls]+=lazy[rt];
        st[rs]+=lazy[rt];
        lazy[ls]+=lazy[rt];
        lazy[rs]+=lazy[rt];
        lazy[rt]=0;
    }
}

void build(int rt,int L,int R)
{
    if(L==R)
    {
        st[rt]=-a[L].se;
        return;
    }
    int mid=(L+R)>>1;
    build(ls,L,mid);
    build(rs,mid+1,R);
    pushUp(rt);
}

void update(int rt,int l,int r,int L,int R,int val)
{
    if(l<=L&&R<=r)
    {
        st[rt]+=val;
        lazy[rt]+=val;
        return;
    }
    pushDown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)
        update(ls,l,r,L,mid,val);
    if(r>mid)
        update(rs,l,r,mid+1,R,val);
    pushUp(rt);
}

int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=n;++i)
        scanf("%d%d",&a[i].fi,&a[i].se);
    for(int i=1;i<=m;++i)
        scanf("%d%d",&b[i].fi,&b[i].se);
    for(int i=1;i<=p;++i)
        scanf("%d%d%d",&mo[i].x,&mo[i].y,&mo[i].z);
    sort(a+1,a+1+n);
    sort(b+1,b+1+m);
    sort(mo+1,mo+1+p);
    build(1,1,n);
    ll ans=-4e18;
    int now=1;
    for(int i=1;i<=m;++i)
    {
        while(now<=p&&mo[now].y<b[i].fi)
        {
            int pos=upper_bound(a+1,a+1+n,make_pair(mo[now].x,inf))-a;
            if(pos<=n) update(1,pos,n,1,n,mo[now].z);
            now++;
        }
        ans=max(ans,st[1]-b[i].se);
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值