牛客练习赛62
Contest Info
Solved | A | B | C | D | E | F |
---|---|---|---|---|---|---|
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"); } }
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); }
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]); }
补题暂时告一段落~
虽然我很弱,但是还是想吐槽一下,牛客比赛的题解真的挺少的,起码相比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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步