美文网首页
CodeForces | Codeforces Global R

CodeForces | Codeforces Global R

作者: 0与1的邂逅 | 来源:发表于2019-04-14 22:55 被阅读0次

    不知不觉,也有一个多月没更新过了,现在打算一周更一次,整合自己在这一周的做题收获。(做题也只能是做很少的部分)

    在上周中打了Codeforces1119——Codeforces Global Round 2,被打自闭了,后面又花了一些时间补了前五道题目。

    PS:由于版面问题,题目就以截图形式,需要原题的同学可以自行去CF上查看。

    A. Ilya and a Colorful Walk

    题解:
    1. 题意:
      有n个房子,编号为1~n,相邻编号的房子相邻(1与n不相邻),这n个房子的颜色分别是c_1c_2、······、c_n,至少有两个房子的颜色是不同的。
      每两个相邻之间的房子的距离为1,要求当两个房子i、j(i≠j)的颜色c_i≠c_j时,这两个房子之间的最大距离。

    2. 题解:【贪心】
      要求最大距离,那么两个房子i、j必须更加靠近两端断点1和n。
      令i=1,j从2~n开始,遍历一遍,求出此时最大的距离max_1。
      再令j=n,i从1~n-1开始,遍历一遍,求出此时最大的距离max_2。
      再比较max_1和max_2的最大值,即为解。
      (即从头、尾对数组c分别进行一次遍历,求出最大的距离)

    参考代码:
    #include<iostream>
    #include<cstdio>
    using namespace std;
    int c[300010];
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&c[i]);
        }
        int count=0;
        int ans=0;
        for(int i=n;i>1;i--)
        {
            if(c[i]!=c[1])
            {
                count=i-1;
                break;
            }
        }
        ans=max(ans,count);
        for(int i=1;i<=n;i++)
        {
            if(c[n]!=c[i])
            {
                count=n-i;
                break;
            }
        }
        ans=max(ans,count);
        //printf("i:%d;j:%d\n",i,j);
        printf("%d\n",ans);
    }
    

    B. Alyona and a Narrow Fridge【思维】

    题解:
    1. 题意:
      现在有个H∗2的立体冰箱,可以在任何一个格子顶部放隔板,现在有一些牛奶盒按顺序放入冰箱(牛奶盒不能叠起来,只能放在隔板上),第_i个牛奶盒为a_i*1的长方形,问最多能放几个牛奶盒。

    2. 题解:
      将这些牛奶盒按照高度a_i升序排序,假设现在a数组有a_1a_2a_3三个元素(牛奶盒的高度),从右到左遍历数组a,如果a_3<=ha_3对应的牛奶盒可以放入冰箱中,那么与a_3相邻的a_2必定可以放入冰箱中(将其放在a_3的旁边),这时再考虑剩余的a_1,如果a_3+a_1< = h,说明a_1也可以放入冰箱中。

      【突破口:升序排序,从右到左遍历,如果a_i可以放入冰箱,那么a_{i-1}必定可以放入冰箱,即代码中的j-=2

      注意这里牛奶盒是按照顺序开始放入冰箱的,所以,每输入一个a_i,就需要对a数组进行一遍排序。

    参考代码:
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n;
    int h;
    int a[1010];
    bool cmp(int x,int y)// sort的比较函数,这里可忽略
    {
        if(x<y)return true;
        else return false;
    }
    
    int main()
    {
        freopen("input.txt","r",stdin);
        freopen("output.txt","w",stdout);
        scanf("%d%d",&n,&h);
        while(~scanf("%d%d",&n,&h))
        {
            int i,j;
            int flag=0;
            int ans;
            for(i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
                if(flag==0)
                {
                    sort(a+1,a+i+1,cmp);
                    //for(int k=1;k<=i;k++)
                    //printf("%d:%d ",k,a[k]);
                    //puts("");
                    long long sum=0;
                    for(j=i;j>=1;j-=2)
                    {
                        sum+=a[j];
                    }
                    if(sum<=h)
                    {
                        if(i==n)ans=n+1;// 如果加上最后一个牛奶盒,仍然<= h,那个最后一个牛奶盒也算入其中(这里n+1是因为输出结果时需要减去1)
                    }
                    else if(sum>h)// 当超过冰箱的高度h,ans记录第一个不满足的牛奶盒的索引
                    {
                        flag=1;
                        ans=i;
                    }
                }
            }
            printf("%d\n",ans-1);// 最后需要减去1(因为此时ans为不满足条件的第一个牛奶盒的索引)
        }
        fclose(stdin);
        fclose(stdout);
    }
    

    C. Ramesses and Corner Inversion【奇偶性、规律】

    题解:
    1. 题意:
      给你两个01矩阵(只含有0、1的矩阵),你只能执行一种操作:就是取上面矩阵中的子矩阵,将子矩阵的四个角的值由零变一,由一变零。求能否通过上述操作让矩阵A等于矩阵B。
    2. 题解:
    解法:【规律性】

    观察发现,

    • 如果任意一列(行)的任意两个1变为0,那么此时这一列(行)的奇偶性不会改变。
    • 如果一列(行)中的恰有一个1和一个0需要改变,那么1->00->1,奇偶性仍然没有变。

    综上的发现,如果矩阵A每一行、每一列的奇偶性与矩阵B对应的每一行、每一列的奇偶性相同,那么就说明可以通过上述的操作,从矩阵A变为矩阵B。

    判断奇偶性,可以使用位操作中的&操作
    if target$1==1:说明target为奇数
    if target&1==0:说明target为偶数

    参考代码:【代码略长。。。逃~】
    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    int mat_a[505][505];// 矩阵A
    int mat_b[505][505];// 矩阵B
    int row_a[505];// 记录矩阵A每一行的奇偶性
    int column_a[505];// 记录矩阵A每一列的奇偶性
    int row_b[505];// 记录矩阵B每一行的奇偶性
    int column_b[505];// 记录矩阵B每一列的奇偶性
    int n,m;
    
    int main()
    {
        //scanf("%d%d",&n,&m);
        freopen("input.txt","r",stdin);
        freopen("output.txt","w",stdout);
        while(~scanf("%d%d",&n,&m))
        {
            int count_a=0;
            int count_b=0;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    scanf("%d",&mat_a[i][j]);
                    count_a+=mat_a[i][j];
                }
                row_a[i]=(count_a&1);
                count_a=0;
            }
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    scanf("%d",&mat_b[i][j]);
                    count_b+=mat_b[i][j];
                }
                row_b[i]=(count_b&1);
                count_b=0;
            }
            count_a=0,count_b=0;
            for(int j=1;j<=m;j++)
            {
                for(int i=1;i<=n;i++)
                {
                    count_a+=mat_a[i][j];
                    count_b+=mat_b[i][j];
                }
                column_a[j]=(count_a&1);
                column_b[j]=(count_b&1);
                count_a=0,count_b=0;
            }
            int flag=0;
            for(int i=1;i<=n;i++)
            {
                if(row_a[i]!=row_b[i])
                {
                    flag=1;
                    break;
                }
            }
            if(!flag)
            {
                for(int j=1;j<=m;j++)
                {
                    if(column_a[j]!=column_b[j])
                    {
                        flag=1;
                        break;
                    }
                }
                if(flag)puts("No");
                else puts("Yes");
            }
            else puts("No");
        }
        fclose(stdin);
        fclose(stdout);
    }
    

    D. Frets On Fire【贡献计算 & 二分查找】

    题解:参考博客
    1. 题意:这里就不过多赘述了(主要是因为懒),看一下题目的Note,其实就很明显了。
    2. 题解:
      题目给出多组的L、R,判断从第L列到第R列中,不重复的数字有多少个。(还是说了一下题意,orz)
      根据题意,我们可以发现s_i而言,值越大,其对结果的贡献就可能最大。多个相同的s_i,只会对结果产生一次贡献。

    s数组进行升序排序,在算一个点的贡献时:

    1. 与后一个有重叠:
      s[i]+r>=s[i+1]+l,即r−l>=s[i+1]−s[i]时,s[i]s[i+1]重叠的部分,都算在s[i+1]里,则s[i]的贡献为s[i+1] - s[i]
    2. 无重叠时,贡献为r - l + 1
    3. s[n]的贡献一定是r - l + 1

    最后再处理一下,计算前i个元素对应的贡献值的和,方便进行二分查找,详细看代码。

    贡献计算:
    我的理解是:有若干个可能的条件,对结果产生贡献,但是这些条件有些贡献是相同的,在计算总的贡献时,就需要剔除重复的贡献。

    参考代码:
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    long long s[100010];
    long long contri[100010];
    long long sum[100010];
    int n;
    int q;
    long long l,r;
    bool cmp(long long x,long long y)// sort的比较函数,这里可忽略
    {
        if(x<y)return true;
        else return false;
    }
    
    int main()
    {
        freopen("input.txt","r",stdin);
        freopen("output.txt","w",stdout);
        while(~scanf("%d",&n))
        {
            for(int i=1;i<=n;i++)scanf("%lld",&s[i]);
            sort(s+1,s+n+1,cmp);
            long long m=unique(s+1,s+n+1)-(s+1);// unique去除重复的s数组元素
            //printf("unique:%d\n",m);
            for(int i=1;i<m;i++)contri[i]=s[i+1]-s[i];// contri:记录贡献值
            sort(contri+1,contri+m,cmp);// 对贡献值升序排序
            for(int i=1;i<m;i++)sum[i]=sum[i-1]+contri[i];// 计算前i个的总贡献
            scanf("%d",&q);
            while(q--)
            {
                long long ans=0;
                scanf("%lld%lld",&l,&r);
                /*
                // 直接遍历,TLE,需要二分查找
                for(int i=1;i<m;i++)
                {
                    long long temp=contri[i]<(r-l+1)?contri[i]:(r-l+1);
                    ans+=temp;
                }
                ans+=(r-l+1);
                */
                // upper_bound:(大于)二分查找,找到最一个比r-l的位置,即第一个r-l+1的位置
                int pos=upper_bound(contri+1,contri+m,r-l)-contri;
                ans=sum[pos-1]+(r-l+1)*(m-pos+1);
                // 分成大[于等于r-l+1]和[小于r-l+1]两段,相加即为最后的结果
                printf("%lld ",ans);
            }
            puts("");
        }
        fclose(stdin);
        fclose(stdout);
    } 
    

    E. Pavel and Triangles【思维】

    题解:
    1. 题意:
      有一个数组a_0a_1a_2……a_{n-1},表示长度为2^02^12^2……2^{n-1}分别的木棒的数量,问:这些木棍最多可以组成的三角形的个数。
    2. 题解:
      首先,根据三角形三边关系,2^02^12^2这三边不能组成一个三角形,进一步推论,可以发现任意三个i、j、ki≠j && i≠k&&j≠k),2^i2^j2^k不可能组成一个三角形。
      所以,要组成三角形,只能是等腰三角形(2^i,2^j,2^j)或者等边三角形(2^j,2^j,2^j)

    构成三角形要么等边,要么等腰,等腰的话另外一边只能取小的,
    如果小的有剩余,当然拿出2个大的来构成等腰更划算,如果没有那么就本身构成等边。
    即temp=min(num/2,last);

    参考代码:
    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    int n;
    long long num;
    int main()
    {
        freopen("input.txt","r",stdin);
        freopen("output.txt","w",stdout);
        //scanf("%d",&n);
        while(~scanf("%d",&n))
        {
            long long last=0,ans=0;// last:前面剩余的
            long long temp;// 与前面剩余的形成等腰三角形的个数
            for(int i=1;i<=n;i++)
            {
                scanf("%lld",&num);
                // 如果小的边有剩余 
                if(last)
                {
                    temp=min(num/2,last);
                    ans+=temp;
                    last-=temp;
                    num-=temp*2;
                }
                ans+=num/3;
                last+=num%3;
            }
            printf("%lld\n",ans);
        }
        fclose(stdin);
        fclose(stdout);
    }
    

    写在最后:

    还有三道题目没有补(其实是不会做),感兴趣的(大佬们)可以去AK,我(蒟蒻)就算了,等后面会做了再来更新吧。

    附上几份题解:

    Per Week,Hard Work!不管别人怎么样,坚持自己的选择,不为什么,只是喜欢。

    相关文章

      网友评论

          本文标题:CodeForces | Codeforces Global R

          本文链接:https://www.haomeiwen.com/subject/ttarwqtx.html