牛客练习赛62

Contest Info


Practice Link

SolvedABCDEF
4/6 Ø O Ø  Ø    
  • O 在比赛中通过 
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A.牛妹的游戏

题意:

给定一个无向图,问这个图中是否存在三元环,或者这个图的补图中是否存在三元环

思路:

背景:

  拉姆塞是位天才的英国科学家,只活了26岁。在他去世的1930年,他发表了一篇学术论文,其副产物就是所谓拉姆塞理论。
  拉姆塞理论可以用通常的语言来表述。在一个集会上,两个人或者彼此认识,或者彼此不认识,拉姆塞得出结果是说,当集会人数大于或等于6时,则必定有3个人,他们或者彼此者认识或者彼此都不认识。6称为拉姆塞数,记r(3,3)。进一步当集会人数大于或等于18时,则必定有4个人,他们或者彼此都认识或者彼此都不认识,用记号表示就是r(4,4)=18。可是集会有多少人,才能有5个人都彼此认识或都不认识呢?至今为此,r(5,5)的精确数目我们还不知道,至于其他的r(n,n)当然就更不清楚了。不过,我们的确证明r(n,n)是一个有限数,的确存在,甚至有精确的上界和下界。只是其中究竟哪一个是拉姆塞数,就不得而知了。因此,求r(n,n)的精确值是我们的头一个难题。
  拉姆塞理论还有进一步的推广,一个最简单的推广是r(s,t),也就是集会至少有多少人,才能有s个人互相都认识或者t个人互相都不认识。可以证明r(s,t)=r(t,s),因此,我们不妨假定s≤t。现在知道的精确的r(s,t)的值极少,只有如下的9种情形:r(3,3)=6 r(3,4)=9 r(3,5)=14 r(3,6)=18 r(3,7)=23 r(3,8)=28 r(3,9)=36 r(4,4)=18 r(4,5)=25
而且我们还知道r(3,t)的一个上界:$r(3,t)≤\frac{t^2+3}{2}$
Ramsay理论是图论四大难题之一

 不妨假设绿方已经控制了所有蓝方没有控制的链,此时存在一个结论:当 $n>5$ 时答案必定为 $yes$。这就是拉姆塞结论,下面给出简单证明:

假设 $6$个据点分别为 $A,B,C,D,E,F$,根据鸽巢原理可知,在$A$连向其它据点的控制链中,必然至少有 $3$条链被同一方控制,不妨假设它们为 $AB,AC,AD$。如此一来只要 $BC,BD,CD$ 中有任意一条链也被这一方控制,则可以形成控制区域;如果这三条链都没有被这一方控制,也就意味着它们都被对方控制了,则它们同样可以形成控制区域。

于是这道题只有在 $n\leq 5$ 时会出现答案为 $no$ 的情况,这部分直接暴力判断即可

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 100;
int T, n, m, x, y;
int mp[maxn][maxn];
int main(){
    scanf("%d", &T);
    while(T--){
        memset(mp, 0, sizeof(mp));
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++){
            scanf("%d%d", &x, &y);
            if(n<6) mp[x][y] = mp[y][x] = 1;
        }
        if(n>=6){
            printf("yes\n");
            continue;
        }
        bool flag = false;
        for(int i = 1; i <= n; i++){
            for(int j = i+1; j <= n; j++){
                for(int k = j+1; k <= n; k++){
                    int x = mp[i][j]+mp[j][k]+mp[k][i];
                    if(x==0||x==3) flag = true;
                }
            }
        }
        if(flag) printf("yes\n");
        else printf("no\n");
    } 
}
View Code

 

C.牛牛染颜色

题意:

将树上一些节点染成黑色,满足任意两个黑色节点的 $lca$ 也被染成黑色。

求染色方案数对 $10^9 + 7$取模

思路:

出题人解释说这是道树形$dp$的题目,这块还有待强化

读完题目后自然会发现:

  1.若当前节点$u$被染色,那么它子树内的点可以任意选择染色或者不染色

  2.否则,只能选择其中一颗子树的节点被染色或者一颗都不选

那么很容易设计出状态$dp_{u, 1/0}$表示当前节点$u$是否被染色的合法方案数

根据上面两个描述得到如下转移方程:

  1.$dp_{u,1} = \prod_{v\in son} (dp_{v,0}+dp_{v,1})$

  2.$dp_{u,0} = 1+\sum_{v\in son} (dp_{v,1}+dp_{v,0}-1)$

注意:$dp_{v,0}$包括所有$v$所在子树所有节点都不被染色的情况,因此要$-1$

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 1e6+100, mod = 1e9+7; 
int T, n, u, v;
int head[maxn], cnt;
ll dp[maxn][2];
struct node{
    int to, nxt;
}e[maxn<<1];
void add(int u, int v){
    e[++cnt] = {v, head[u]};
    head[u] = cnt; 
}
void dfs(int u, int fa){
    dp[u][1] = dp[u][0] = 1;//dp[u][0]=1表示空集的情况 
    for(int i = head[u]; i; i = e[i].nxt){
        int v = e[i].to;
        if(v==fa) continue;
        dfs(v, u);
        dp[u][1] = (dp[u][1]*(dp[v][0]+dp[v][1]))%mod;
        dp[u][0] = (dp[u][0]+(dp[v][1]+dp[v][0]-1))%mod;
    }
}
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n-1; i++){
        scanf("%d%d", &u, &v);
        add(u, v), add(v, u);
    }
    dfs(1, 0);
    printf("%lld", (dp[1][0]+dp[1][1])%mod);
}
View Code

 

D.牛牛的呱数

题意:

给定$n$个数字串,可以按任意顺序拼接,每个串可以使用无限次,使得拼接的的数字串长度最短且被$p$整除

思路:

赛后思考了很久,我觉得下面的这种方法自己比较能接受,比赛中很多过D题的人也是这么做的

考虑把新增的数拼接到已有数的右边,那么对于给定的一个数$num_i(i \in n)$,我们把它拼接到数串(模意义下)$[0-p]$的右边,就可以得出这个数对答案的贡献,接下来我们只需要求出模意义下从$0$拼接到$0$的最小花费即可,怎么做呢?

设$dp[i][j]$为$i,j$模意义下,$i$和$s$拼接成$j$的最小花费,建立有向图,跑最短路得出答案

#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int n, p;
char s[1000010];
int dp[210][210];
int main(){
    scanf("%d%d", &n, &p);
    memset(dp, 0x3f, sizeof(dp));
    for(int i = 1; i <= n; i++){
        scanf("%s", s+1);
        int len = strlen(s+1), val = 0, tmp = 1;
        for(int j = 1; j <= len; j++){
            val = (val*10+s[j]-'0')%p;
            tmp = (10*tmp)%p;
        }
        for(int j = 0; j < p; j++)
            dp[j][(j*tmp+val)%p] = min(dp[j][(j*tmp+val)%p], len);
    }
    for(int k = 0; k < p; k++)
        for(int i = 0; i < p; i++)
            for(int j = 0; j < p; j++)
                dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]);
    
    if(dp[0][0]==inf) printf("-1");
    else printf("%d", dp[0][0]);        
}
View Code

 

 

补题暂时告一段落~

虽然我很弱,但是还是想吐槽一下,牛客比赛的题解真的挺少的,起码相比Codeforces来说少很多,刚好又身处弱校找不人问就很头疼,之后可能主要补CF的题

不过下午问了两个博主D题,都及时回复了我,无敌感动,然而可能有的地方还是理解不来,就跟暑假多校训练一样的,只能先搁在那儿了~


Refrence:

https://bbs.emath.ac.cn/thread-311-1-1.html

https://en.jinzhao.wiki/wiki/Ramsey%27s_theorem

https://ac.nowcoder.com/discuss/416603?type=101&order=time&pos=&page=1&channel=-1

https://ac.nowcoder.com/acm/problem/blogs/204897

https://blog.csdn.net/qq_41286356/article/details/105744853?fps=1&locationNum=2

 

posted @ 2020-04-25 11:22  sparkyen  阅读(209)  评论(0编辑  收藏  举报