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\) 是容易的

得到结论后,假设 \(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矩阵
如下构造:

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,问题转化为,计数:
- 对于 必选 \(0\) 且不选 \(1\),有多少联通块
- 对于必选 \(0,1\) 且不选 \(2\),有多少联通块
对于必选 \(0,1,2...,i\) 的计数,可以通过树上 DP 统计,但是对于不选 \(i+1\),是难以组合和统计的
所以可以修改计数方式:
- 对于 必选 \(0\),有多少联通块
- 对于必选 \(0,1\),有多少联通块
- 对于必选 \(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();
}
}

浙公网安备 33010602011771号