• Codeforces Round #191 (Div. 2) 解题报告


    ------------

    A. Flipping Game

    有n个整数a1, a2, ..., an,每个整数只可能为0或1。

    选择一个区间[i, j](i<=j)翻转,翻转表示X=1-X。

    目标是求一次翻转后1的最大数目。

    ----

    将0变为1,将1变为-1,1次翻转后1的最大数目=原序列中1的数目+最大连续子序列和。

    问题转化为求最大连续子序列和,dp解决。

    注意一定要翻转一次,这是个坑。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    const int INF=1e9;
    int a[111]={0};
    int f[111]={0};
    int n;
    
    int main()
    {
        int ans;
        int bas;
        while (cin>>n)
        {
            ans=-INF;
            bas=0;
            for (int i=1;i<=n;i++)
            {
                cin>>a[i];
                if (a[i]) {a[i]=-1; bas++; }
                else if (!a[i]) a[i]=1;
            }
            for (int i=1;i<=n;i++)
            {
                f[i]=max(f[i-1]+a[i],a[i]);
                ans=max(ans,f[i]);
            }
            cout<<bas+ans<<endl;
        }
        return 0;
    }

    ------------

    B. Hungry Sequence

    寻找一个长度为n(1 ≤ n ≤ 105)的饥饿序列。

    一个序列为饥饿序列,当且仅当:

    ①若i<j,则ai<aj。

    ②若i<j,则aj不能被ai整除。

     (1 ≤ ai ≤ 107)

    找到任意一个序列即可。

    ----

    最简单的序列:n+1, n+2, n+3....n+n。

    简单证明:(n+i)*2=n+n+i*2>n+n

    另一种方法:

    前sqrt(n)个素数两两相乘,排序后输出即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    int n;
    int main()
    {
        while (cin>>n)
        {
            for (int i=1;i<=n;i++) cout<<n+i<<" ";
            cout<<endl;
        }
        return 0;
    }

    ------------

    C. Magic Five

    一个n位长的数字S,从中删除几个数字(可以不删除但不能全部删除),得到一个能被5整除的数,称为幻数。

    问能得到幻数的方法有多少种(结果模1000000007)。如果删除的数字位置不同则两种方法不同。

    S由一个特殊形式给出。

    输入一个仅包含数字的字符串a (1 ≤ |a| ≤ 105),和一个整数k(1 ≤ k ≤ 109),S由k个a连接而成。

    ----

    当一个数字个位数为0或5的时候才能被5整除。

    将一个能被5整除的n位数删除一些数字,使其还能被5整除有:

    种方案。

    假设a的第i位为0或5,则以该位为尾数的方案数为2^i。

    对于第i位的k次重复,方案数为:2^i+2^(i+n)+2^(i+2*n)+...+2^(i+k*n)即一个首项为2^i公比为2^n的等比数列之和。


    接下来用到的一些定理:


    a/b%M=a*(b的逆元)%M。

    证明:


    M=10^9+7为素数,b的逆元为b^(p-2)%p。

    证明:


    则以a的第i位为尾数的方案数:


    更优的代码:

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    
    typedef long long LL;
    const LL MOD=1000000007;
    
    LL quick_mod(LL a,LL b,LL m)
    {
        LL ans=1;
        while (b)
        {
            if (b&1)
            {
                ans=(ans*a)%m;
                b--;
            }
            b/=2;
            a=a*a%m;
        }
        return ans;
    }
    
    int main()
    {
        char s[111111];
        int n,k;
        LL ans,q;
        while (cin>>s>>k)
        {
            ans=0;
            n=strlen(s);
            q=quick_mod(2,n,MOD);
            for (int i=0;i<n;i++)
            {
                if (s[i]=='0'||s[i]=='5')
                {
                    ans+=quick_mod(2,i,MOD)%MOD;
                    ans%=MOD;
                }
            }
            ans=ans*(quick_mod(q,k,MOD)-1+MOD)%MOD*quick_mod(q-1,MOD-2,MOD)%MOD;
            cout<<ans<<endl;
        }
        return 0;
    }


    优化前:

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    
    typedef long long LL;
    const LL MOD=1000000007;
    
    LL quick_mod(LL a,LL b,LL m)
    {
        LL ans=1;
        while (b)
        {
            if (b&1)
            {
                ans=(ans*a)%m;
                b--;
            }
            b/=2;
            a=a*a%m;
        }
        return ans;
    }
    
    int main()
    {
        char s[111111];
        int n,k;
        LL ans,q;
        while (cin>>s>>k)
        {
            ans=0;
            n=strlen(s);
            q=quick_mod(2,n,MOD);
            for (int i=0;i<n;i++)
            {
                if (s[i]=='0'||s[i]=='5')
                {
                    ans+=quick_mod(2,i,MOD)%MOD
                    *(quick_mod(q,k,MOD)-1+MOD)%MOD
                    *quick_mod(q-1,MOD-2,MOD)%MOD;
                    ans%=MOD;
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    ------------

    D. Block Tower

    有一个n*m的矩形网格,其中#不能建造,其余是空的。

    有两种塔:蓝塔(容纳100人),红塔(容纳200人)。

    红塔只有在与蓝塔相邻(公边)时才能建造。

    有三种操作:

    B x y:在(x,y)建立一座蓝塔。

    R x y:在(x,y)建立一座红塔。

    D x y:摧毁(x,y)处的塔,摧毁一座塔对其它已建成的塔没有影响。

    找到一个能容纳最多人口的操作序列 。不要求序列最短。

    ----

    为了使人口最大应该尽可能的建造红塔。

    可以发现网络中的每一个连通区域只需要保留1座蓝塔。

    由于不要求序列最短,可以先将一个区域建造满蓝塔,再按照建造的路径拆除蓝塔改建红塔。

    最后只保留第一个蓝塔即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    
    using namespace std;
    
    const int maxn=555;
    const int direct[4][2]={ {1,0},{0,1},{-1,0},{0,-1} };
    
    struct ANS_NODE{
        int x;
        int y;
        char c;
        ANS_NODE(int _x,int _y,char _c):x(_x),y(_y),c(_c){}
    };
    
    char map[maxn][maxn];
    bool visit[maxn][maxn];
    vector<ANS_NODE>ans;
    vector<ANS_NODE>::iterator it;
    int n,m;
    
    bool check(int x,int y)
    {
        if (x>=1&&x<=n&&y>=1&&y<=m) return true;
        return false;
    }
    
    void dfs(int x,int y,int d)
    {
        visit[x][y]=true;
        ans.push_back(ANS_NODE(x,y,'B'));
        for (int i=0;i<4;i++)
        {
            int dx=x+direct[i][0];
            int dy=y+direct[i][1];
            if (check(dx,dy)&&map[dx][dy]!='#'&&!visit[dx][dy])
            {
                dfs(dx,dy,d+1);
            }
        }
        if (d)
        {
            ans.push_back(ANS_NODE(x,y,'D'));
            ans.push_back(ANS_NODE(x,y,'R'));
        }
    }
    
    int main()
    {
        while (cin>>n>>m)
        {
            ans.clear();
            memset(visit,0,sizeof(visit));
            for (int i=1;i<=n;i++) cin>>(map[i]+1);
            for (int i=1;i<=n;i++)
            {
                for (int j=1;j<=m;j++)
                {
                    if (!visit[i][j]&&map[i][j]!='#') dfs(i,j,0);
                }
            }
            cout<<ans.size()<<endl;
            for (it=ans.begin();it!=ans.end();it++)
            {
                cout<<it->c<<" "<<it->x<<" "<<it->y<<endl;
            }
        }
        return 0;
    }

    ------------

    E. Axis Walking

    有n个(n<=24)数字a1,a2,a3...an,和为d。

    中间过程和有k个数不能出现,问从0加到d有多少种方法。

    ----

    可以用状态压缩dp卡时间过。。

    sum[S]表示集合S中的数字之和。

    f[S]表示当前状态为S时的移动方案数。

    当sum[S]为不能出现的数字时,f[S]=0;

    其他情况 f[S]+=f[S^(1<<i)] ,i属于集合S。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    typedef long long LL;
    
    const int maxn=24;
    const int maxs=1<<24;
    const LL MOD=1000000007;
    
    int n,m;
    LL a[maxn]={0};
    LL sum[maxs]={0};
    LL b[maxn]={0};
    LL f[maxs]={0};
    int main()
    {
        //init()
        cin>>n;
        for (int i=0; i<n; i++) cin>>a[i];
        cin>>m;
        for (int i=0; i<m; i++) cin>>b[i];
        //prepare()
        sum[0]=0;
        for (int s=1; s<(1<<n); s++)
        {
            int k=__builtin_ctz(s);
            sum[s]=sum[s^(1<<k)]+a[k];
            for (int i=0; i<m; i++)
                if (sum[s]==b[i]) f[s]=-1;
        }
        f[0]=1;
        for (int s=1; s<(1<<n); s++)
        {
            if (f[s]<0) f[s]=0;
            else
            {
                for (int i=0; i<n; i++)
                    if (s&(1<<i)) f[s]+=f[s^(1<<i)];
                f[s]%=MOD;
            }
        }
        cout<<f[(1<<n)-1]<<endl;
        return 0;
    }
    

    ------------

  • 相关阅读:
    mongodb修改和删除操作
    mongodb常用查询语句
    ejs常用功能函数
    Rails + React +antd + Redux环境搭建
    nodejs安装和卸载
    Git 建立仓库及常用命令速查表
    使用MVC5+Entity Framework6的Code First模式创建数据库并实现增删改查功能
    gem devise配置
    Ubuntu16.04安装postgresql9.4及pgadmin3图形管理界面
    Rails Migration Data Model栏位修改及数据类型介绍
  • 原文地址:https://www.cnblogs.com/cyendra/p/3226286.html
Copyright © 2020-2023  润新知