• Codeforces Round #376 (Div. 2)


    Problem D  80-th Level Archeology

    题目大意:有n行,每行有ki 个数,你没次能进行一次操作就是将所有数加1,这些数的上限是c,如果大于c则变成1,

    问你有没有存在一个操作次数m使得每行的字典序上升。

    思路:我们找出每两行之间决定他们字典序大小的两个数,每两个都能求出一次操作数的范围,然后就是看存不存在

    一个范围所有区间都覆盖,差分标记一下就好啦。

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define mk make_pair
    using namespace std;
    const int N=5*1e5+5;
    const int M=2e6+5;
    vector<int> a[M],b[M];
    vector<int> c[M],d[M];
    int vis[M];
    int n,w,cnt;
    int main()
    {
        scanf("%d%d",&n,&w); w--;
        for(int i=1;i<=n;i++)
        {
            int k; scanf("%d",&k);
            a[i].resize(k);
            for(int j=0;j<k;j++)
            {
                int g; scanf("%d",&g);
                a[i][j]=g-1;
            }
        }
        for(int i=1;i<n;i++)
        {
            int up=min(a[i].size(),a[i+1].size());
            bool flag=false;
            for(int j=0;j<up;j++)
            {
                if(a[i][j]>a[i+1][j])
                {
                    b[a[i][j]].push_back(a[i+1][j]);
                    flag=true;
                    break;
                }
                else if(a[i][j]<a[i+1][j])
                {
                    b[a[i][j]].push_back(a[i+1][j]),flag=true;
                    break;
                }
            }
            if(!flag && a[i].size()>a[i+1].size())
            {
                puts("-1");
                return 0;
            }
        }
        for(int i=0;i<1e6;i++)
        {
            if(!b[i].size()) continue;
            int mx=-1,mn=1e6+5,mx2=-1,mn2=1e6+5,add=w-i+1;
            for(int j:b[i])
            {
                mx=max(mx,j);
                mn=min(mn,j);
                mx2=max(mx2,(j+add)%(w+1));
                mn2=min(mn2,(j+add)%(w+1));
            }
            cnt++;
            if(mn>i)
            {
                c[0].push_back(cnt);
                d[w-mx].push_back(cnt);
                c[w-i+1].push_back(cnt);
                d[2*w-mx].push_back(cnt);
            }
            else
            {
                c[add].push_back(cnt);
                d[add+w-mx2].push_back(cnt);
            }
        }
        int ans=0;
        for(int i=0;i<1e6;i++)
        {
            for(int j:c[i])
            {
                if(!vis[j]) ans++;
                vis[j]++;
            }
            if(ans==cnt)
            {
                printf("%d
    ",i);
                return 0;
            }
            for(int j:d[i])
            {
                if(vis[j]==1) ans--;
                vis[j]--;
            }
        }
        puts("-1");
        return 0;
    }
    View Code

    Problem E Funny Game

    题目大意:给出n个数,有两个人,每次从这些数的最左边拿走k个数,范围为2~m。特别之处在于,拿走后会把

    拿走的数的和作为一个新的数放在最左边,要求两个人拿走数之差的最大值。

    思路:先求一遍前缀和,问题就变成了:一排数,两个人轮流取数,保证取的位置递增(且从第二个数开始取),

    每个人要使自己取的数的和尽量大,求在最优策略下取的max(先手和-后手和)。

    dp[ i ]表示,将前i个看成一堆,先手的最优答案,又有dp[ n-1 ]=sum[ n ],那么我们就能从后往前推,导出状态

    转移方程,dp[ i ]=max(sum[j] - dp[ j ])  i < j <=n,这个状态转移方程的意思表示,对dp[ i ]来说先手取了第j个sum

    然后还要减去第二个人先手的最优值。

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 const int N=2*1e5+5;
     5 const int inf=1e18;
     6 int n;
     7 ll a[N],sum[N],dp[N];
     8 int main()
     9 {
    10     scanf("%d",&n);
    11     for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    12     for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
    13     ll mx=sum[n];
    14     for(int i=n-1;i>=1;i--)
    15     {
    16         dp[i]=mx;
    17         mx=max(mx,sum[i]-dp[i]);
    18     }
    19     printf("%lld
    ",dp[1]);
    20     return 0;
    21 }
    View Code
  • 相关阅读:
    20175226 2018-2019-2 《Java程序设计》第二周学习总结
    存储管理-页面置换算法(页面淘汰算法)
    存储管理-存储组织
    进程管理-死锁问题
    操作系统-进程管理
    操作系统:进程管理、存储管理、文件管理、作业管理、设备管理
    第十一章 集合框架
    匿名内部类
    第10章 java常用类
    第8章 反射
  • 原文地址:https://www.cnblogs.com/CJLHY/p/7989234.html
Copyright © 2020-2023  润新知