牛客练习赛62题解

题目链接

A.牛妹的游戏

题意:
n 个 点 的 完 全 图 , m 条 边 属 于 一 类 阵 营 n个点的完全图,m条边属于一类阵营 nm
另 外 m 条 边 属 于 另 一 类 阵 营 另外m条边属于另一类阵营 m
给 出 你 n , m 和 这 m 条 边 , 问 相 同 阵 营 是 否 存 在 一 个 三 元 环 给出你n,m和这m条边,问相同阵营是否存在一个三元环 nmm
题解:
通 过 画 图 观 察 可 以 发 现 通过画图观察可以发现
n < = 5 的 时 候 n<=5的时候 n<=5
将 每 个 点 依 次 相 连 , 首 尾 相 接 , 就 不 会 存 在 同 阵 营 三 元 环 将每个点依次相连,首尾相接,就不会存在同阵营三元环
但 是 当 n > 5 的 时 候 但是当n>5的时候 n>5
如 果 再 次 这 样 , 你 就 会 发 现 , 可 以 在 内 部 成 为 一 个 三 元 环 如果再次这样,你就会发现,可以在内部成为一个三元环
但 是 如 果 加 上 三 元 环 的 其 中 一 个 边 , 就 会 构 造 成 另 外 一 个 阵 营 的 三 元 环 但是如果加上三元环的其中一个边,就会构造成另外一个阵营的三元环
看 题 解 这 道 题 是 个 拉 姆 塞 结 论 , 大 概 就 是 n 大 于 5 必 有 三 元 环 这 种 意 思 看题解这道题是个拉姆塞结论,大概就是n大于5必有三元环这种意思 n5
n < 5 的 时 候 , 就 可 以 用 邻 接 矩 阵 存 图 n<5的时候,就可以用邻接矩阵存图 n<5
直 接 枚 举 任 意 三 个 点 , 看 这 三 个 点 之 间 的 边 同 属 于 这 m 条 边 或 者 都 不 属 于 即 可 直接枚举任意三个点,看这三个点之间的边同属于这m条边或者都不属于即可 m

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int g[10][10];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        if(n>5){
            cout<<"yes"<<endl;
            for(int i=0;i<m;i++){int u,v;cin>>u>>v;}
            continue;
        }
        memset(g,0,sizeof g);
        for(int i=0;i<m;i++){
            int u,v;
            cin>>u>>v;
            g[u][v]=g[v][u]=1;
        }
        bool f=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++){
                    if(i==j||j==k||i==k)continue;
                    if(g[i][j]==g[i][k]&&g[i][k]==g[j][k]&&g[j][k]==g[i][j])f=1;
                }
        if(f)cout<<"yes"<<endl;
        else cout<<"no"<<endl;
    }


    return 0;
}



B.病毒扩散

题意:
( 0 , 0 ) 处 有 一 个 感 染 者 (0,0)处有一个感染者 00
在 每 1 s 每 个 点 都 会 往 右 边 的 一 个 点 和 上 边 的 一 个 点 扩 散 一 个 感 染 者 在每1s每个点都会往右边的一个点和上边的一个点扩散一个感染者 1s
q 次 询 问 在 ( x , y ) 在 t   s 的 时 候 有 多 少 感 染 者 q次询问在(x,y)在t~s的时候有多少感染者 qx,yt s
题解:
问 题 模 型 转 换 问题模型转换
可 以 转 换 成 某 个 人 t   s 的 时 间 到 达 ( x , y ) 的 方 案 数 可以转换成某个人t~s的时间到达(x,y)的方案数 t sxy
每 秒 它 可 以 不 动 , 右 移 或 者 上 移 每秒它可以不动,右移或者上移
右 移 和 上 移 就 代 表 正 常 的 每 秒 的 扩 散 右移和上移就代表正常的每秒的扩散
不 动 就 代 表 在 某 个 点 的 下 一 秒 或 者 以 后 几 秒 对 ( x , y ) 的 扩 散 数 不动就代表在某个点的下一秒或者以后几秒对(x,y)的扩散数 xy
问 题 模 型 转 换 好 了 , 就 很 好 写 了 问题模型转换好了,就很好写了
使 用 组 合 数 , C ( t , x ) ∗ C ( t − x , y ) 使用组合数,C(t,x)*C(t-x,y) 使C(t,x)C(tx,y)
从 t s 里 选 出 x s 和 y s 右 移 和 上 移 即 可 从ts里选出xs和ys右移和上移即可 tsxsys

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll fact[maxn],inv1[maxn];
ll Pow(ll a, ll b){
	ll ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
//逆元
ll inv(ll b){
    return Pow(b,mod-2)%mod;
}



ll C(ll n,ll m){
    if(m==0||m==n) return 1;
    ll res=(fact[n]*inv1[m]%mod*inv1[n-m])%mod;
    return res;
}

void init() {
	fact[0] = 1;
	for (int i = 1; i < maxn; i++) {
		fact[i] = fact[i - 1] * i %mod;
	}
	inv1[maxn - 1] = Pow(fact[maxn - 1], mod - 2);
	for (int i = maxn - 2; i >= 0; i--) {
		inv1[i] = inv1[i + 1] * (i + 1) %mod;
	}
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    init();
    while(t--){
        ll x,y,t;
        cin>>x>>y>>t;
        if(x+y>t)cout<<0<<endl;
        else cout<<(C(t,x)*C(t-x,y))%mod<<endl;
    }


    return 0;
}



C.牛牛染颜色

题意:
给 一 个 有 n 个 结 点 的 树 , 1 是 根 结 点 给一个有n个结点的树,1是根结点 n1
每 个 结 点 可 以 染 色 成 黑 色 或 者 白 色 每个结点可以染色成黑色或者白色
如 果 两 个 结 点 是 黑 色 , 那 么 他 们 两 个 结 点 的 最 近 公 共 祖 先 也 必 须 是 黑 色 如果两个结点是黑色,那么他们两个结点的最近公共祖先也必须是黑色
问 有 多 少 种 涂 色 方 案 问有多少种涂色方案
题解:
树 形 D P 树形DP DP
用 d p [ u ] [ 0 / 1 ] 表 示 树 上 结 点 u 染 黑 色 ( 1 ) 或 者 白 色 ( 0 ) 的 方 案 数 用dp[u][0/1]表示树上结点u染黑色(1)或者白色(0)的方案数 dp[u][0/1]u(1)(0)
如 果 这 个 结 点 是 黑 色 , 那 么 他 的 任 何 一 个 子 树 都 可 以 是 黑 色 或 者 白 色 如果这个结点是黑色,那么他的任何一个子树都可以是黑色或者白色
即 d p [ v ] [ 1 ] + d p [ v ] [ 0 ] 即dp[v][1]+dp[v][0] dp[v][1]+dp[v][0]
如 果 这 个 结 点 是 白 色 , 那 么 他 的 子 树 如果这个结点是白色,那么他的子树
只 能 全 部 白 色 , 或 者 只 有 一 个 黑 色 的 只能全部白色,或者只有一个黑色的
由 于 要 考 虑 空 集 防 止 重 复 计 算 的 情 况 , 所 以 要 对 每 次 情 况 进 行 减 一 , 最 后 结 束 了 加 上 这 个 空 集 由于要考虑空集防止重复计算的情况,所以要对每次情况进行减一,最后结束了加上这个空集
所 以 可 以 得 到 转 移 方 程 所以可以得到转移方程

d p [ u ] [ 0 ] = ( d p [ u ] [ 0 ] + d p [ v ] [ 1 ] + d p [ v ] [ 0 ] − 1 ) dp[u][0]=(dp[u][0]+dp[v][1]+dp[v][0]-1) dp[u][0]=(dp[u][0]+dp[v][1]+dp[v][0]1)
d p [ u ] [ 1 ] = d p [ u ] [ 1 ] ∗ ( d p [ v ] [ 1 ] + d p [ v ] [ 0 ] ) dp[u][1]=dp[u][1]*(dp[v][1]+dp[v][0]) dp[u][1]=dp[u][1](dp[v][1]+dp[v][0])

最 后 的 答 案 即 d p [ 1 ] [ 1 ] + d p [ 1 ] [ 0 ] 最后的答案即dp[1][1]+dp[1][0] dp[1][1]+dp[1][0]

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

struct edge{
    int v,nx;
}e[maxn<<1];
int head[maxn];
int cnt;
void add(int u,int v){
    e[++cnt].v=v;
    e[cnt].nx=head[u];
    head[u]=cnt;
}
ll dp[maxn][2];
void dfs(int u,int fa){
    dp[u][0]=dp[u][1]=1;
    for(int i=head[u];i;i=e[i].nx){
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
        dp[u][0]=(dp[u][0]+dp[v][1]+dp[v][0]-1)%mod;
        dp[u][1]=dp[u][1]*(dp[v][1]+dp[v][0])%mod;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        add(u,v);add(v,u);
    }
    dfs(1,0);
    cout<<(dp[1][0]+dp[1][1])%mod<<endl;
    return 0;
}



D. 牛牛的呱数

题意:
给 你 n 个 数 , 每 个 数 的 长 度 < = 1 e 6 给你n个数,每个数的长度<=1e6 n<=1e6
每 个 数 有 无 数 个 , 任 意 组 合 , 组 合 成 p 的 倍 数 每个数有无数个,任意组合,组合成p的倍数 p
问 这 样 组 成 数 最 小 的 长 度 问这样组成数最小的长度
题解:
由 于 n < = 100 , p < = 200 由于n<=100,p<=200 n<=100,p<=200
所 以 可 以 考 虑 B F S 所以可以考虑BFS BFS
枚 举 每 种 情 况 , 维 护 一 下 再 向 p 取 余 的 结 果 x 和 长 度 y 此 时 的 最 短 长 度 枚举每种情况,维护一下再向p取余的结果x和长度y此时的最短长度 pxy
每 次 连 接 这 n 个 数 , 然 后 加 入 队 列 每次连接这n个数,然后加入队列 n
每 次 新 的 d x = ( x + 1 0 y ∗ a [ i ] ) % p 每次新的dx=(x+10^y*a[i])\%p dx=(x+10ya[i])%p
然 后 B F S 每 次 当 x = 0 的 时 候 取 这 个 结 果 , 最 终 取 最 小 值 即 可 然后BFS每次当x=0的时候取这个结果,最终取最小值即可 BFSx=0

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int a[110];
int dis[210][210];
int len[110];
int b[maxn];
struct node{
    int a,b,len;
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,p;
    cin>>n>>p;
    b[0]=1;
    for(int i=1;i<maxn;i++)b[i]=b[i-1]*10%p;
    for(int i=1;i<=n;i++){
        string s;
        cin>>s;
        len[i]=s.length();
        for(int j=0;j<len[i];j++)
            a[i]=(a[i]*10+s[j]-'0')%p;
    }
    int ans=inf;
    memset(dis,inf,sizeof dis);
    queue<node> q;
    for(int i=1;i<=n;i++){
        int da=a[i];
        int db=b[len[i]];
        int dlen=len[i];
        if(dis[da][db]>dlen){
            dis[da][db]=dlen;
            q.push((node){da,db,dlen});
        }
    }
    while(!q.empty()){
        node x=q.front();
        q.pop();
        if(x.len>dis[x.a][x.b])continue;
        if(!x.a)ans=min(ans,x.len);
        for(int i=1;i<=n;i++){
            int da=(x.a+a[i]*x.b)%p;
            int db=x.b*b[len[i]]%p;
            int dlen=x.len+len[i];
            if(dlen<dis[da][db]){
                dis[da][db]=dlen;
                q.push((node){da,db,dlen});
            }
        }
    }
    if(ans>=inf)cout<<-1;
    else cout<<ans;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值