Codeforces Round 608 Div2

A

题意

水题

思路

水题

代码

#include<bits/stdc++.h>

using namespace std;

int main()
{
    int a,b,c,d,e,f,res=0;
    scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f);
    if(e>f)
    {
        res+=min(a,d)*e;
        d-=min(a,d);
        res+=min(min(b,c),d)*f;
    }
    else
    {
        res+=min(min(b,c),d)*f;
        d-=min(min(b,c),d);
        res+=min(a,d)*e;
    }
    printf("%d\n",res);
}

B

题意

给一个序列,由W,B组成,代表白色和黑色。每次操作可以选择两个相邻的位置,然后把它们的颜色取反(不是交换),可以操作任意次。找出能否使得序列变全黑或全白的操作。

思路

按目标全黑或全白扫一遍即可

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=205;

char input[MAX],s[MAX];
vector<int>v;
map<char,char>mp;
int main()
{
    mp['W']='B';
    mp['B']='W';
    int n;
    scanf("%d%s",&n,input);
    strcpy(s,input);
    int tot=0;
    for(int i=0; i<n-1; i++)
        if(s[i]=='W')
        {
            s[i]='B';
            s[i+1]=mp[s[i+1]];
            tot++;
            v.push_back(i+1);
        }
    if(s[n-1]=='B')
    {
        printf("%d\n",tot);
        for(int i=0; i<v.size(); i++)
            printf("%d ",v[i]);
        printf("\n");
        return 0;
    }
    tot=0;
    v.clear();
    strcpy(s,input);
    for(int i=0; i<n-1; i++)
        if(s[i]=='B')
        {
            s[i]='W';
            s[i+1]=mp[s[i+1]];
            tot++;
            v.push_back(i+1);
        }
    if(s[n-1]=='W')
    {
        printf("%d\n",tot);
        for(int i=0;i<v.size();i++)
            printf("%d ",v[i]);
        printf("\n");
        return 0;
    }
    printf("-1\n");
}

C

题意

有一个学校,坐标x,y。有若干学生,家在不同的地方,上学走和学校构成矩形内的任意曼哈顿路径,求某一点坐标,使得上学路上可以经过该点的学生最多。

思路

显然可以分为四个象限和四个坐标轴。分别计算一下上面的学生人数。然后取最大值。结果就是最大的那个区域的坐标轴离学校远一格。

代码

#include<bits/stdc++.h>

using namespace std;

int quadrant[5],axes[5];

int main()
{
    int n,sx,sy,x,y;
    scanf("%d%d%d",&n,&sx,&sy);
    while(n--)
    {
        scanf("%d%d",&x,&y);
        if(x==sx)
        {
            if(y>sy)
                axes[2]++;
            else
                axes[4]++;
        }
        if(y==sy)
        {
            if(x>sx)
                axes[1]++;
            else
                axes[3]++;
        }
        if(x>sx&&y>sy)
            quadrant[1]++;
        if(x<sx&&y>sy)
            quadrant[2]++;
        if(x<sx&&y<sy)
            quadrant[3]++;
        if(x>sx&&y<sy)
            quadrant[4]++;
    }
    int maxx=-1;
    for(int i=1; i<=4; i++)
    {
        int cur=axes[i]+quadrant[i]+quadrant[i==1?4:i-1];
        if(maxx<cur)
        {
            maxx=cur;
            if(i==1)x=sx+1,y=sy;
            if(i==2)x=sx,y=sy+1;
            if(i==3)x=sx-1,y=sy;
            if(i==4)x=sx,y=sy-1;
        }
    }
    printf("%d\n%d %d\n",maxx,x,y);
}

D

题意

n个城堡,你带兵从1-n按顺序攻打。初始有k个士兵,攻打i号城堡需要ai个士兵(只是需要,不会有伤亡)。打完以后可以征兵,i号城堡可以征兵bi个,城堡攻打下来以后可以驻守,驻守需要1个士兵。驻守i号城堡可以得分ci分。此外还有一些秘密通道,u-v,代表可以从u号城堡派一个士兵去驻守v号城堡(保证u>v)。求能否攻打下全部的城堡,若能,则输出最大得分,否则输出-1

思路

首先由于必须全部攻打下来才有得分,所以很容易想到一些贪心的结论:

  1. 征兵一定优于不征兵
  2. 如果当前城堡可以后面再驻守,那后面再驻守一定优于当前就驻守,并且越后驻守越好、

这样决策就只剩下:

  1. 当前城堡不能后面再驻守,决策要不要在这里驻守
  2. 当前城堡有秘密通道,决策要不要派兵回去驻守。

很明显是动态规划的问题。然后从数据范围很容易想到dp[i][j]表示到第i个城堡时有j个士兵时能获得的最大收益。转移就dp[i][j]dp[i-1][j-bi]转移得到。然后再由dp[i][j]dp[i][j-1]转移,取决于当前城堡有没有秘密通道。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=5005;

int a[MAX],b[MAX],c[MAX],dp[MAX][MAX],pre[MAX];
vector<int>out[MAX],in[MAX],cur;

int main()
{
    int n,m,k,u,v;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&a[i],&b[i],&c[i]),pre[i]=pre[i-1]+b[i];
    while(m--)
    {
        scanf("%d%d",&u,&v);
        in[v].push_back(u);
    }
    for(int i=1;i<=n;i++)
    {
        if(!in[i].size())continue;
        sort(in[i].begin(),in[i].end());
        out[in[i].back()].push_back(i);
    }
    memset(dp,-1,sizeof dp);
    dp[0][k]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=a[i]+b[i];j<=pre[i]+k;j++)
        {
            dp[i][j]=dp[i-1][j-b[i]];
            if(dp[i][j]==-1)continue;
            if(!in[i].size()&&j>=1)
                dp[i][j-1]=max(dp[i][j-1],dp[i][j]+c[i]);
            if(out[i].size())
            {
                cur.clear();
                int sum=0;
                for(int l=0;l<out[i].size();l++)
                    cur.push_back(c[out[i][l]]);
                sort(cur.begin(),cur.end(),greater<int>());
                for(int l=0;l<cur.size();l++)
                {
                    sum+=cur[l];
                    if(j-l-1>=0)
                        dp[i][j-l-1]=max(dp[i][j-l-1],dp[i][j]+sum);
                    if(j-l-2>=0&&!in[i].size())
                        dp[i][j-l-2]=max(dp[i][j-l-2],dp[i][j]+sum+c[i]);
                }
            }
        }
    }
    int res=-1;
    for(int i=0;i<=pre[n]+k;i++)
        res=max(res,dp[n][i]);
    printf("%d\n",res);
}

E

题意

定义一个函数f
\[ \begin{eqnarray} f(x)=\begin{cases} \frac x2 &x为偶数\\ x-1 &x为奇数 \end{cases} \end{eqnarray} \]
对于一个数x连续调用f(x),得到一个序列。如对15得到15,14,7,6,3,2,1。现在给出n,k,求一个数x,使得x1-n中各数的序列中,至少有k个序列包含x

思路

很容易想到答案具有单调性。但是通过样例可以发现不是单纯的单调性,要分奇偶。奇数答案和偶数答案内部都具有单调性。所以分奇偶二分答案,复杂度也是可行的。所以问题转化为判断一个数x,在给定的序列中到底出现几次。把序列画成一棵树可以找到规律。

如图可得规律,x的出现次数就是以x为根节点的子树的节点个数(最大值要限制在n之内)。由规律可以log(n)的找出x的出现次数。总复杂度log^2(n)

代码

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

ll n,k;

ll check(ll x)
{
    ll a,b,ans=0;
    if(x&1ll)
        a=b=x;
    else
        a=x,b=x+1;
    while(a<=n)
    {
        ans+=min(n,b)-a+1;
        a=a*2;
        b=b*2+1;
    }
    return ans;
}

int main()
{
    scanf("%I64d%I64d",&n,&k);
    ll L=1,R=n/2+(n&1),res=-1;
    while(L<=R)
    {
        ll mid=(L+R)>>1;
        if(check(mid*2-1)>=k)
        {
            L=mid+1;
            res=max(res,mid*2-1);
        }
        else
            R=mid-1;
    }
    L=1,R=n/2;
    while(L<=R)
    {
        ll mid=(L+R)>>1;
        if(check(mid<<1)>=k)
        {
            L=mid+1;
            res=max(res,mid<<1);
        }
        else
            R=mid-1;
    }
    printf("%I64d\n",res);
}

猜你喜欢

转载自www.cnblogs.com/cryingrain/p/12405403.html
今日推荐