牛客练习赛 62

A.牛妹的游戏

Ramsey定理人话解释任意六个人中要么至少三个人认识,要么至少三个不认识。

结论简要证明:

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

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=500010;
int n,m;
int e[10][10];
int main()
{
    IO;
    int T=1;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        memset(e,0,sizeof e);
        if(n>5)
        {
            for(int i=1;i<=m;i++)
            {
                int a,b;
                cin>>a>>b;
            }
            cout<<"yes\n";
            continue;
        }
        for(int i=1;i<=m;i++)
        {
            int a,b;
            cin>>a>>b;
            e[a][b]=e[b][a]=1;
        }
        bool ok=0;
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                for(int k=j+1;k<=n;k++)
                    if(e[i][j]&&e[j][k]&&e[k][i]||!e[i][j]&&!e[j][k]&&!e[k][i])
                        ok=1;
        if(ok) cout<<"yes\n";
        else cout<<"no\n";
    }
    return 0;
}

B.病毒扩散

打表找规律,杨辉三角。
第四秒后扩张现象如下图

[ 1 × 1 4 × 1 6 × 1 4 × 1 1 × 1 4 × 1 6 × 2 4 × 3 1 × 4 6 × 1 4 × 3 1 × 6 4 × 1 1 × 4 1 × 1 ] \begin{bmatrix} 1×1&4×1&6×1&4×1&1×1\\4×1&6×2&4×3&1×4\\6×1&4×3&1×6\\4×1&1×4\\1×1 \end{bmatrix} 1×14×16×14×11×14×16×24×31×46×14×31×64×11×41×1
好像不是那么明显,我们把乘号左边和右边的数分别拿出来
左 边 : [ 1 4 6 4 1 4 6 4 1 6 4 1 4 1 1 ] 左边:\begin{bmatrix} 1&4&6&4&1\\4&6&4&1\\6&4&1\\4&1\\1 \end{bmatrix} :146414641641411

右 边 : [ 1 1 1 1 1 1 2 3 4 1 3 6 1 4 1 ] 右边:\begin{bmatrix} 1&1&1&1&1\\1&2&3&4\\1&3&6\\1&4\\1 \end{bmatrix} :111111234136141
不难发现这两个矩阵的这些数和组合数(杨辉三角)有关
考虑位置为 ( x , y ) (x,y) (x,y)时间是 t t t的情况下:
左边的数 C t x + y C_{t}^{x+y} Ctx+y,右边的数 C x + y x C_{x+y}^x Cx+yx那么最终答案就是 C t x + y × C x + y x C_{t}^{x+y}×C_{x+y}^x Ctx+y×Cx+yx
预处理阶乘和逆元即可 O ( 1 ) O(1) O(1)得到每个位置的答案
我看不懂的官方证明转化

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=5010;
const ll mod=998244353;
ll fact[N],infact[N];
ll qmi(ll a,ll b,ll p)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=res*a%p;
        b>>=1;
        a=a*a%p;
    }
    return res;
}
void init()
{
    fact[0]=infact[0]=1;
    for(int i=1;i<N;i++)
    {
        fact[i]=fact[i-1]*i%mod;
        infact[i]=qmi(fact[i],mod-2,mod);
    }
}
int n;
int main()
{
    IO;
    int T=1;
    //cin>>T;
    init();
    while(T--)
    {
        cin>>n;
        while(n--)
        {
            int x,y,t;
            cin>>x>>y>>t;
            if(x+y>t) cout<<0<<'\n';
            else
            {
                ll res=1;
                res=res*fact[t]*infact[x+y]%mod*infact[t-x-y]%mod;
                res=res*fact[x+y]%mod*infact[x]%mod*infact[y]%mod;
                cout<<res<<'\n';
            }
        }
    }
    return 0;
}

C.牛牛染颜色

树形dp
状态表示: f ( i , 0 / 1 ) f_{(i,0/1)} f(i,0/1)表示选择/不选择 u u u 这个节点后以 u u u 为根的子树的合法方案数。
状态转移:
若选择 u u u 这个节点,则子树内可以随便选点,每个子树独立乘法原理可得转移 f ( i , 1 ) = ∏ j ∈ s o n f ( j , 0 ) + f ( j , 1 ) f_{(i,1)}=\prod_{j\in son} f_{(j,0)}+f_{(j,1)} f(i,1)=jsonf(j,0)+f(j,1)
若不选择 u u u 这个节点,则最多选择某一个子树,由加法原理可得转移 f ( i , 0 ) = 1 + ∑ j ∈ s o n ( f ( j , 0 ) + f ( j , 1 ) − 1 ) f_{(i,0)}=1+\sum_{j\in son}(f_{(j,0)}+f_{(j,1)}-1) f(i,0)=1+json(f(j,0)+f(j,1)1)(​ f ( i , 0 ) f_{(i,0)} f(i,0)包含空集的方案,所以在枚举子树统计的时候每颗子树贡献的答案要减 1 1 1,但最后也要把空集的情况算上,还要加个 1 1 1

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1000010,mod=1e9+7;
int h[N],e[2*N],ne[2*N],idx;
ll f[N][2];
int n;
void add(int a,int b)
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
void dfs(int u,int fa)
{
    f[u][0]=f[u][1]=1;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==fa) continue;
        dfs(j,u);
        f[u][1]=(f[u][1]*(f[j][0]+f[j][1]))%mod;
        f[u][0]=(f[u][0]+f[j][1]+f[j][0]-1)%mod;
    }
}
int main()
{
    IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
        cin>>n;
        memset(h,-1,sizeof h);
        for(int i=1;i<n;i++)
        {
            int a,b;
            cin>>a>>b;
            add(a,b),add(b,a);
        }
        dfs(1,-1);
        cout<<(f[1][0]+f[1][1])%mod<<'\n';
    }
    return 0;
}

D. 牛牛的呱数

对于大数,基本上都是取模达到我们想要的目的。
由此可以把原串取模后的答案记录下来,并且记录它的长度(边权),和别的串相接的过程就类似从一个状态到另一个状态,只需要预处理 1 0 k % p 10^k\%p 10k%p的结果跑最短路即可。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
const int N=210;
int ten[1000010];
struct node
{
    int val,len;
}a[N];
int dist[N],n,p;
bool st[N];
void dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    priority_queue<pii,vector<pii>,greater<pii> >q;
    for(int i=1;i<=n;i++) 
    {
        dist[a[i].val]=min(dist[a[i].val],a[i].len);
        q.push({a[i].len,a[i].val});
    }
    while(q.size())
    {
        int t=q.top().second;q.pop();
        if(st[t]) continue;
        st[t]=1;
        for(int i=1;i<=n;i++)
        {
            int now=(t*ten[a[i].len]%p+a[i].val)%p;
            if(dist[now]>dist[t]+a[i].len)
            {
                dist[now]=dist[t]+a[i].len;
                q.push({dist[now],now});
            }
        }
    }
}
int main()
{
    IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
        cin>>n>>p;
        ten[0]=1;
        for(int i=1;i<=1000000;i++) ten[i]=ten[i-1]*10%p;
        for(int i=1;i<=n;i++)
        {
            string s;
            cin>>s;
            a[i].len=s.size();
            reverse(s.begin(),s.end());
            ll base=1;
            ll now=0;
            for(auto t:s)
            {
                now=(now+base*(t-'0')%p)%p;
                base=base*10%p;
            }
            a[i].val=now%p;
        }
        dijkstra();
        if(dist[0]==0x3f3f3f3f) cout<<"-1\n";
        else cout<<dist[0]<<'\n';
    }
    return 0;
}

要加油哦~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值