zoukankan      html  css  js  c++  java
  • Codeforces 1068

    链接:http://codeforces.com/contest/1068


    A - Birthday - [计算题]

    题意:一共 $N$ 种硬币,我已经有其中 $K$ 种,我的 $M$ 个朋友每人送我等数量的硬币,且送来的每一枚硬币都不属于同一种,要求最后我收到手上的硬币至少有 $L$ 种是我没有的,求每个人最少送几枚硬币。

    题解:即求满足 $K + L le aM le N$ 的最小正整数 $a$。

    AC代码:

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    ll n,m,k,l;
    int main()
    {
        cin>>n>>m>>k>>l;
        if(m>n)
        {
            cout<<-1<<endl;
            return 0;
        }
        if(k+l<=m)
        {
            cout<<1<<endl;
            return 0;
        }
        ll a;
        if((k+l)%m==0) a=(k+l)/m;
        else a=(k+l)/m+1;
        if(a*m>n) cout<<-1<<endl;
        else cout<<a<<endl;
    }

    B - LCM - [求因数个数]

    题意:给定正整数 $b(b le 10^{10})$,对于 $1 le a le 10^{18}$,求 $frac{{{mathop{ m lcm} olimits} (a,b)}}{a}$ 的不同取值个数。

    题解:$frac{{{mathop{ m lcm} olimits} (a,b)}}{a} = frac{{ab}}{{agcd (a,b)}} = frac{b}{{gcd (a,b)}}$,即求 $b$ 的因数个数。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll n;
    int main()
    {
        cin>>n;
        ll cnt=0;
        for(ll i=1;i*i<=n;i++)
        {
            if(n%i==0)
            {
                if(i*i==n) cnt++;
                else cnt+=2;
            }
        }
        cout<<cnt<<endl;
    }

    C - Colored Rooks - [构造题]

    题意:

    给定一个 $10^9 imes 10^9$ 的正方形网格,现有 $n$ 种颜色,给若干个棋子上色(每种颜色都要涂至少一个棋子),又给出 $m$ 个颜色对 $(x,y)$ 表示这两个颜色是协调的

    现在对于同一种颜色内的棋子集合,以及两个和谐的颜色对构成的棋子并集,都要满足连通性质,该性质的意思即:“首先规定棋子只可以走直线的任意距离,且可以完全不理睬非同集合内的棋子,但只有当它遇到同集合内的另一个棋子时才可以转弯。而该集合中任意一个棋子,都它可以走到集合内的任意其他棋子的位置。”

    现在你要给出任意一种可行的放置棋子的方案。

    题解:

    首先,第 $i$ 行全部给第 $i$ 种颜色,不得放其他颜色。为了防止某种颜色没有涂棋子,可以先在 $(i,i)$ 位置放上颜色为 $i$ 的棋子。

    然后对于给出的任意两个协调颜色,依次在后面的 $n+1, n+2, cdots$ 列里放上对应行数的两枚棋子。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m;
    vector<int> ans[105];
    
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++) ans[i].push_back(i);
        int col=n+1;
        for(int i=1,u,v;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            if(u==v) continue;
            ans[u].push_back(col);
            ans[v].push_back(col);
            col++;
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d
    ",ans[i].size());
            for(int k=0;k<ans[i].size();k++) printf("%d %d
    ",i,ans[i][k]);
        }
    }

    D - Array Without Local Maximums - [dp]

    题意:给定一个长度为 $n(n le 1e5)$ 的数组 $a[1:n]$,$a[i]$ 的值域为 $1$ 到 $200$,且每个数的旁边必须有一个不小于它的数。而有些数字被擦掉了,现在问共有多少种可行的填充方案。

    题解:

    看到这题第一感觉就dfs暴力搜索,然而时间复杂度显然不行,因此考虑dp。

    考虑 $dp[i][x][0,1,2]$ 的表示的状态为已经确定了第 $i$ 个数字为 $x$,而 $0,1,2$ 分别表示第 $i-1$ 个数 $w$ 是小于、等于或大于 $x$。其存储的值是其状态下的方案数。

    那么就有状态转移方程(当然这是建立在第 $i+1$ 位可以填 $y$ 的前提下的):

    $egin{array}{l} dp[i + 1][y][0] = sumlimits_{x = 1}^{y - 1} {dp[i][x][0,1,2]} \ dp[i + 1][y][1] = dp[i][y][0,1,2] \ dp[i + 1][y][2] = sumlimits_{x = y + 1}^{200} {dp[i][x][1,2]} \ end{array}$

    边界条件,考虑到第 $1$ 个数左边是空的,相当于左边是一个更小的数,因此有(依然是建立在第一位能填入 $x$ 的前提下):

    $egin{array}{l} dp[1][x][0] = 1 \ dp[1][x][1] = 0 \ dp[1][x][2] = 0 \ end{array}$

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=998244353;
    const int maxn=1e5+5;
    int n,a[maxn];
    ll sum,dp[maxn][203][3];
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int x=1;x<=200;x++)
        {
            if(a[1]!=-1 && a[1]!=x) dp[1][x][0]=dp[1][x][1]=dp[1][x][2]=0;
            else dp[1][x][0]=1, dp[1][x][1]=dp[1][x][2]=0;
        }
        for(int i=2;i<=n;i++)
        {
            sum=0;
            for(int y=1;y<=200;y++)
            {
                if(a[i]!=-1 && a[i]!=y) dp[i][y][0]=0;
                else dp[i][y][0]=sum;
                sum+=(dp[i-1][y][0]+dp[i-1][y][1]+dp[i-1][y][2])%mod;
                sum%=mod;
            }
    
            for(int y=1;y<=200;y++)
            {
                if(a[i]!=-1 && a[i]!=y) dp[i][y][1]=0;
                else dp[i][y][1]=(dp[i-1][y][0]+dp[i-1][y][1]+dp[i-1][y][2])%mod;
            }
    
            sum=0;
            for(int y=200;y>=1;y--)
            {
                if(a[i]!=-1 && a[i]!=y) dp[i][y][2]=0;
                else dp[i][y][2]=sum;
                sum+=(dp[i-1][y][1]+dp[i-1][y][2])%mod;
                sum%=mod;
            }
        }
        ll ans=0;
        for(int x=1;x<=200;x++) ans+=(dp[n][x][1]+dp[n][x][2])%mod, ans%=mod;
        cout<<ans<<endl;
    }

    E - Multihedgehog - [BFS]

    题意:

    一个连通无向图,若仅有一个节点度数不小于 $3$,其他所有节点度数均为 $1$,则称为“刺猬”。现在定义“$k$-刺猬”,为:

      “$1$-刺猬”即普通“刺猬”;

      对于“$k$-刺猬”,其由“$k-1$-刺猬”变化而来,将“$k-1$-刺猬”的所有度数为 $1$ 的顶点换成一个“$1$-刺猬”的中心,即构成“$k$-刺猬”。

    现在给你一张图和一个 $k$,让你判断这张图是否为“$k$-刺猬”。

    题解:

    首先,当 $k=11$ 时,节点数最少也需要 ${sum_{i=0}^{11}{3^i}} = 265720$ 个顶点,显然已经不够了,所以对于 $k ge 11$ 的情况可以直接判断不行。

    接下来,可以考虑以所有度数为零的节点为起始,将它们的深度定义为 $0$。然后自底向上地进行BFS(或者说BFS的一种变形?),同时不断地删除叶子结点。

    对于每个节点,都需要判断它的深度和度数是否可行,还要判断其父亲节点是否只有一个。

    对于最后一个没有父亲节点的根节点,判断他的深度是否等于 $k$。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=2e5+10;
    
    int n,k;
    int degree[maxn];
    
    struct Edge{
        int u,v;
        int next;
    };
    Edge E[maxm];
    int head[maxn],ne;
    void init()
    {
        ne=0;
        memset(head,0,sizeof(head));
    }
    void addedge(int u,int v)
    {
        ++ne;
        E[ne].u=u, E[ne].v=v;
        E[ne].next=head[u];
        head[u]=ne;
    }
    
    int dist[maxn];
    bool del[maxn],vis[maxn];
    bool bfs()
    {
        queue<int> Q;
        for(int i=1;i<=n;i++)
        {
            if(degree[i]==1) Q.push(i), vis[i]=1, dist[i]=0;
            else vis[i]=0;
        }
        memset(del,0,sizeof(del));
        while(!Q.empty())
        {
            int u=Q.front(); Q.pop();
            del[u]=1; //将节点删除
    
            int cnt=0; //记录父亲节点个数
            for(int i=head[u];i;i=E[i].next)
            {
                int v=E[i].v;
    
                if(del[v]) continue;
                else cnt++;
                if(cnt>1) return false; //父亲节点应当只有一个
    
                if(!vis[v])
                {
                    vis[v]=1;
                    dist[v]=dist[u]+1;
                    Q.push(v);
                }
                else
                {
                    if(dist[v]!=dist[u]+1) return false;
                }
            }
    
            if(cnt==0) { //当前节点是根节点
                if(degree[u]<3) return false;
                if(dist[u]!=k) return false;
            }
            else if(degree[u]>1) { //当前节点不是根节点也不是叶子结点
                if(degree[u]<=3) return false;
            }
        }
        return true;
    }
    
    int main()
    {
        cin>>n>>k;
        init();
        memset(degree,0,sizeof(degree));
        for(int i=1,u,v;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
            degree[u]++;
            degree[v]++;
        }
        if(n<4 || k>=11) printf("No
    ");
        else printf("%s
    ",bfs()?"Yes":"No");
    }
  • 相关阅读:
    机器学习---算法---K-近邻算法
    机器学习---算法---逻辑回归
    进程线程---简单解释
    【codecs】音视频编解码开源项目大汇总
    【life】选择程序员,就是选择一种生活
    【PE】逆向工程(反编译)
    【OpenSource】开源管理平台BlackDuck简介
    【Analysis】开源工程Binwalk:固件分析利器
    【Tools/VS】VS2010 代码块快速折叠快捷键
    【Audio】开源音频库G711和MP3Dec网址
  • 原文地址:https://www.cnblogs.com/dilthey/p/9853026.html
Copyright © 2011-2022 走看看