2026牛客寒假算法基础集训营2 8/10

A. 比赛安排

判断最大最小值的差值是否大于 \(1\) 即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int a,b,c;
    cin>>a>>b>>c;

    if(max({a,b,c})-min({a,b,c})<=1) cout<<"YES";
    else cout<<"NO";
    cout<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

B. NCPC

先统计每个数出现的次数

如果最大值出现奇数次,则每个最大值都能赢,其他的一定输

否则最大值一定输,其他的都能赢

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<int> a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }

    int mx=*max_element(a.begin()+1,a.end());
    int cnt=0;

    for(int i=1;i<=n;i++){
        if(a[i]==mx) cnt++;
    }

    bool f=cnt&1;

    for(int i=1;i<=n;i++){
        if(!f){
            if(a[i]==mx) cout<<0;
            else cout<<1;
        }
        else{
            if(a[i]!=mx) cout<<0;
            else cout<<1;
        }
    }

    cout<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

I. 01回文

因为是 01 矩阵,所以回文都是形如:00000,11111,10001,011110 这样的

所以,如果数组全等,则每个位置都可以

如果数组中 0 和 1 的出现次数都大于等于 \(2\),则每个位置都可以

如果 0 只出现了一次,则从 0 出发,是找不到另一个 0 的,此时不行。1 同理

点击查看代码
#include<bits/stdc++.h>
// #define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n,m;
    cin>>n>>m;
    vector g(n+1,vector<int>(m+1));
    int c0=0,c1=0;

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            char ch;
            cin>>ch;
            g[i][j]=(ch=='1');
            if(g[i][j]) c1++;
            else c0++;
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(g[i][j]==1 && c1==1){
                cout<<"N";
            }
            else if(g[i][j]==0 && c0==1){
                cout<<"N";
            }
            else cout<<"Y";
        }
        cout<<'\n';
    }

}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

F. x?y?n!

难点在于如何 guess 到并证明异或和最小为 \(n\) 的结论。构造一个异或和为 \(n\)\(x,y\) 是容易的

image

得到结论后,假设 \(n\) 的二进制表示是 1011,那就让 \(x\) 等于 \(n\) 左移 \(n\) 的位数,得到 \(x=10110000\)

\(y=x+n,=10111011\)

二者异或结果为 \(n\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long; 
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;

void solve(){
    int n;
    cin>>n;

    for(int i=32;i>=0;i--){
        if(n>>i & 1){
            cout<<(n<<(i+1))<<" "<<(n<<(i+1))+n<<endl;
            return;
        }
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--) solve();

    return 0;
}

H. 权值计算

比较版的题,算一下每个位置的贡献就行

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<int> a(n+1);
    map<int,int> pre;
    int ans=0;

    for(int i=1;i<=n;i++){
        cin>>a[i];
        int k=n-i+1;
        ans+=(i-pre[a[i]])*(1+k)*k/2;
        pre[a[i]]=i;
    }

    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

E. 01矩阵

如下构造:

image

J. 终于再见

关键在于,边的总数是 \(m\),所以最多会有 \(\sqrt m\) 种不同的繁荣度

对每种繁荣度做一次 BFS 即可,复杂度 \(O(n*\sqrt m)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n,m;
    cin>>n>>m;

    vector<vector<int>> g(n+1);
    vector<int> ans(n+1,inf);
    vector<vector<int>> lv(m+1);

    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    for(int i=1;i<=n;i++){
        lv[g[i].size()].push_back(i);
    }

    auto dijkstra=[&](int level)-> void {
        vector<int> dist(n+1,inf);
        queue<int> q;

        for(auto u:lv[level]){
            q.push(u);
            dist[u]=0;
        }

        while(q.size()){
            auto u=q.front();
            q.pop();
            for(auto v:g[u]){
                if(dist[u]+1<dist[v]){
                    dist[v]=dist[u]+1;
                    q.push(v);
                }
            }
        }

        for(int i=1;i<=n;i++){
            if(g[i].size()>=level) continue;
            ans[i]=min(ans[i],dist[i]);
        }
    };

    for(int i=m;i>=1;i--){
        if(lv[i].size()==0) continue;
        dijkstra(i);
    }

    for(int i=1;i<=n;i++){
        if(ans[i]==inf) cout<<-1;
        else cout<<ans[i];
        cout<<' ';
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;

    while(ct--){
        solve();
    }
}

D. 数字积木

枚举 mex,问题转化为,计数:

  1. 对于 必选 \(0\) 且不选 \(1\),有多少联通块
  2. 对于必选 \(0,1\) 且不选 \(2\),有多少联通块

对于必选 \(0,1,2...,i\) 的计数,可以通过树上 DP 统计,但是对于不选 \(i+1\),是难以组合和统计的

所以可以修改计数方式:

  1. 对于 必选 \(0\),有多少联通块
  2. 对于必选 \(0,1\),有多少联通块
  3. 对于必选 \(0,1,2\),有多少联通块

则原本的计数中,“必选 \(0\) 且不选 \(1\),有多少联通块”,可以通过新计数方式中的 1 - 2 来得到

\(0\) 所在的节点为根,设 \(f[u]\) 表示,以 \(u\) 为根节点的子树,有多少个包含 \(u\) 的联通块

\(f[root]\) 即为 “1. 对于 必选 \(0\) 且不选 \(1\),有多少联通块” 的结果

对于每次加进去一个新的必选的点,则要把根到这个点这条链上,所有点对答案的贡献,由 \(f[v]+1\) 修改为 \(f[v]\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 1e9+7;

int qmi(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=res*a%p;
        b>>=1;
        a=a*a%p;
    }
    return res;
}

void solve(){
    int n;
    cin>>n;

    vector<int> w(n+1),in(n);
    vector<vector<int>> g(n+1);

    for(int i=1;i<=n;i++){
        cin>>w[i];
        in[w[i]]=i;
    }

    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    int root=in[0];
    vector<int> f(n+1, 1);
    vector<int> fa(n+1);

    auto dfs=[&](auto dfs,int u,int pre)->void {
        fa[u]=pre;
        for(auto v:g[u]){
            if(v==pre) continue;
            dfs(dfs,v,u);
            f[u] = f[u] * (f[v] + 1) % mod;
        }
    };

    dfs(dfs,root,0);
    
    int cur_prod = f[root];
    int pre=cur_prod, ans=0;
    vector<int> st(n+1);
    st[root]=1;

    for(int i=1;i<n;i++){
        int u=in[i];
        while(!st[u]){
            cur_prod = cur_prod * qmi(f[u] + 1, mod - 2, mod) % mod;
            cur_prod = cur_prod * f[u] % mod;
            st[u]=1;
            u=fa[u];
        }

        int cur=cur_prod;
        ans=(ans+(pre-cur+mod)%mod*i)%mod;
        pre=cur;
    }
    
    ans=(ans+pre*n)%mod;
    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;

    while(ct--){
        solve();
    }
}

实际上面的代码还有问题,没有考虑到取模后结果是 \(0\) 的情况

处理方式不太会:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 1e9+7;

int qmi(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=res*a%p;
        b>>=1;
        a=a*a%p;
    }
    return res;
}

struct SafeProd{
    int zero=0, prod=1;
    void mul(int x){
        x%=mod;
        if(!x) zero++;
        else prod=prod*x%mod;
    }
    void div(int x){
        x%=mod;
        if(!x) zero--;
        else prod=prod*qmi(x,mod-2,mod)%mod;
    }
    int val(){
        return zero?0:prod;
    }
};

void solve(){
    int n;
    cin>>n;

    vector<int> w(n+1),in(n);
    vector<vector<int>> g(n+1);

    for(int i=1;i<=n;i++){
        cin>>w[i];
        in[w[i]]=i;
    }

    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    int root=in[0];
    vector<SafeProd> f(n+1);
    vector<int> fa(n+1);

    auto dfs=[&](auto dfs,int u,int pre)->void {
        fa[u]=pre;
        for(auto v:g[u]){
            if(v==pre) continue;
            dfs(dfs,v,u);
            f[u].mul(f[v].val()+1);
        }
    };

    dfs(dfs,root,0);
    
    SafeProd cur_prod = f[root];
    int pre=cur_prod.val(), ans=0;
    vector<int> st(n+1);
    st[root]=1;

    for(int i=1;i<n;i++){
        int u=in[i];
        while(!st[u]){
            cur_prod.div(f[u].val()+1);
            cur_prod.mul(f[u].val());
            st[u]=1;
            u=fa[u];
        }

        int cur=cur_prod.val();
        ans=(ans+(pre-cur+mod)%mod*i)%mod;
        pre=cur;
    }
    
    ans=(ans+pre*n)%mod;
    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;

    while(ct--){
        solve();
    }
}
posted @ 2026-03-13 15:08  LYET  阅读(14)  评论(0)    收藏  举报