2026CCPC网络赛补题记录

第十二届中国大学生程序设计竞赛网络预选赛(CCPC Online 2026)

N.New CCPC Rules

本题送。

分析:

模拟题意即可。

Code:

#include<bits/stdc++.h>
using namespace std;
int T;
int n,m;
char ch[10];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        scanf("%s",ch+1);
        int now=ceil(0.2*n);
        if(ch[2]>='4'||m>=50||m>=now){
            puts("YES");
        }
        else{
            puts("NO");
        }
    }
    return 0;
}

I.Integer Triple

本题场上没有想法,队友做出。

分析:

观察。首先数据很大,要么 \(\log n\) 枚举,要么直接构造。大概率还是构造吧。看式子 \(ab(a+b)\) ,这是个比较特殊的乘积式,整个式子还是轮换的,可以算一下 \(a,b,c\) 的奇偶性。可以发现,\(n\) 不能为奇数。代入 \(a=1,b=-1\) ,可以发现有解,\(c=\frac{n}{2}\) 。

反思:

这是构造题,作为前OI选手确实很不善长。总结如下:

  1. 看数据范围,式子,猜到它大概是构造题。
  2. 分析。根据式子找突破口,如取模(2,3这样的数),化简,带入特殊值等。

这个式子有这样的乘法,加法的结果很适合对2取模,可以分析奇偶性。

这个式子可以尝试化简,发现不好构造。如果难,可以暂不尝试。

带入值。式子复杂,看能不能消掉一些项,让我们能看出答案或解出答案。先尝试消掉 \(ab(a+b)\) 。代 \(a=0\) ,化出来的式子是很不好搞的。带 \(a+b=0\) ,直接代 \(a=1,b=-1\) ,可以有解。并且这是很特殊的答案,观察后面的项可以猜到这大概是突破口,要有这个直觉。最好带入像这样很特殊的答案(如 \(1,-1\) ),带 \(-1,2\) 这样式的数据往往会让式子很难看,大概率是无用功。

还不行,可以小范围打表,往往可以为我们的猜测提供灵感。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
int main(){
    scanf("%lld",&n);
    if((n&1)==1)puts("-1");
    else printf("%d %d %lld\n",1,-1,n>>1);
    return 0;
}

B.Better Dice

本题为本人场切。

分析:

\(b_i\) 设计为什么值的关键就是超过多少个 \(a_i\) 。所以可以直接将 \(a\) 排序。其实 \(\sum{b_i}\) 越小越好,如果超过了 \(k\) ,肯定不行。如果没到 \(k\) ,可以将剩下的随意加给某个数,这样也是一定合法的。所以,\(b_i\) 的可能取值就是各个 \(a_i+1\) 和 \(0\) 。暴力枚举,时间复杂度是 \(O(7^6) \approx 10^5\) ,可以轻松通过。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[10];
ll k;
int tot;
ll sum;
ll b[10];
void dfs(int x){
    if(x==7){
        if(sum>k)return;
        if(tot<19)return;
        if(sum<k)b[6]+=k-sum;
        puts("YES");
        for(int i=1;i<=6;i++)printf("%lld ",b[i]);
        exit(0);
    }
    for(int i=0;i<=6;i++){
        tot+=i;
        sum+=a[i]+1ll;
        b[x]=a[i]+1ll;
        dfs(x+1);
        tot-=i;
        sum-=a[i]+1ll;
    }
}
int main(){
    for(int i=1;i<=6;i++){
        scanf("%lld",&a[i]);
    }
    sort(a+1,a+1+6);
    scanf("%lld",&k);
    dfs(1);
    puts("NO");
    return 0;
}

H.Hidden Permutation

又是构造题。本题为队友给思路,队友跟我讲了以后我觉得很有道理,遂队友场切。队友咋写的我忘了,以下是我自己想的思路。

分析:

这里储存信息有一点困难。因为它会打乱行顺序,所以你得存下位置,每个排列还得不同。既然要不同,我就让每个排列第一个位置不同。怎么存位置?

先让每个排列和给出的排列一样,但第 \(i\) 个排列就让 \(a_1\) 和 \(a_i\) 交换位置。发现除了 \(a_1\) ,其他所有位置只被交换了一次,其它的都是真实数字。所以对于 \(2 \sim n\) 只要统计出现次数 \(\ge 2\) 的数字即可。对于 \(a_1\) ,在 \(2 \sim n\) 中没有出现的就是 \(a_1\) 。这样做的好处就是虽然打乱了行顺序,但列可以把信息保存下来,而第一个数无需保存信息,可以直接推断出来。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=2005;
char op[10];
int n;
int a[N],b[N];
int c[N][N],tong[N];
int main(){
    scanf("%s%d",op+1,&n);
    if(op[1]=='f'){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)b[j]=a[j];
            swap(b[1],b[i]);
            for(int j=1;j<=n;j++)printf("%d ",b[j]);
            puts("");
        }
    }
    else{
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&c[i][j]);
            }
        }
        for(int j=2;j<=n;j++){
            if(c[1][j]==c[2][j])b[j]=c[1][j];
            else if(c[1][j]==c[3][j])b[j]=c[1][j];
            else b[j]=c[2][j];
        }
        for(int i=2;i<=n;i++)tong[b[i]]++;
        for(int i=1;i<=n;i++){
            if(!tong[i])b[1]=i;
        }
        for(int i=1;i<=n;i++)printf("%d ",b[i]);
    }
    return 0;
}

E.EPS.AC

本题为队友给思路,听完队友思路后我觉得他是错的,但队友仍然去打代码,于是队友场切。(看来是我的问题)。emm我感觉我改进了下他的做法(时间更优了),而且我反思了下,他的核心思路确实是对的。

分析:

这个题要给数字分段。先以 \(a\) 为基准,将它重新编号(这样它就刚好是从小到大了)。遍历时记录 \(b_i\) 所对应的编号的最大值,因为当最大值等于 \(i\) 时,\(a\) 和 \(b\) 的前 \(i\) 个刚好对应 \(1 \sim i\) ,此时位置 \(i\) 可以分段。像这样比对 \(a\) 和 \(b\) ,\(a\) 和 \(c\) , \(b\) 和 \(c\) ,就找到了所有分段点,统计即可,时间复杂度 \(O(n)\) 。

你可能会想这样会不会有问题?它明明没有要求每组刚好是按顺序排好在分割的,万一调走一个数可以分更多段呢?但这不存在,因为这样就是前某一个分界线左边的某些数不包括在后某一个分界线左边的数字中。而一条分界线由两个人共同决定,所以两个分界线一定有一个人同时参与了,而对他来说就是从小到大的,不会出现不包括的情况。所以这样是对的。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int read(){
    int f=1,x=0;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return f*x;
}
int T;
int n;
int a[N],b[N],c[N];
int id[N];
bool ans[N];
void init(){
    for(int i=1;i<=n;i++)ans[i]=false;
}
int main(){
    T=read();
    while(T--){
        n=read();
        init();
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=n;i++)b[i]=read();
        for(int i=1;i<=n;i++)c[i]=read();
        for(int i=1;i<=n;i++)id[a[i]]=i;
        int maxn=0;
        for(int i=1;i<=n;i++){
            int bi=id[b[i]];
            maxn=max(maxn,bi);
            if(maxn==i)ans[i]=true;
        }
        maxn=0;
        for(int i=1;i<=n;i++){
            int ci=id[c[i]];
            maxn=max(maxn,ci);
            if(maxn==i)ans[i]=true;
        }
        for(int i=1;i<=n;i++)id[b[i]]=i;
        maxn=0;
        for(int i=1;i<=n;i++){
            int ci=id[c[i]];
            maxn=max(maxn,ci);
            if(maxn==i)ans[i]=true;
        }
        int tot=0;
        for(int i=1;i<=n;i++)tot+=ans[i];
        printf("%d\n",tot);
    }
    return 0;
}

C. Cross the Road

本题完全为队友负责,他差一点就调出来了qwq

K. Keep on the Right Track

本题为本人给出思路(经队友提醒改正看题错误),最后本人场切。by the way,这真的是一个OI风格十分浓厚的题,好典型的最短路计数+拓扑dp的糅合。

分析:

首先,有信号就是以最短路数量为比例,那肯定是要最短路计数的。问到达 \(t\) 的最短路数量,就直接建反图,跑拓扑计数。写法经典,看代码。算期望也经典,可以根据 \(i\) 有没有信号算出走这条路的概率 \(p_{i,u}\) 。设 \(f_u\) 表示从 \(u\) 走到 \(t\) 的期望时间。既然终点只有一个 \(t\) ,那从 \(t\) 到 \(u\) 是一样的。建反图,跑拓扑dp,\(\displaystyle f_i= \sum{(f_u+w_{i,u}) \times p_{i,u}}\) 。时间复杂度 \(O(n \log{mod})\)

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const ll mod=998244353;
const ll inf=0x3f3f3f3f3f3f3f3f;
int read(){
    int f=1,x=0;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return f*x;
}
void exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
}
ll inv(ll a){
    ll x,y;
    exgcd(a,mod,x,y);
    x=(x%mod+mod)%mod;
    return x;
}
int n,m,ed;
bool col[N];
struct node{
    int to,val;
};
vector<node>v1[N],v2[N];  //v1是正图,v2是反图
queue<int>q;
int du[N];
ll dis[N],tot[N];
ll f[N];
int main(){
    n=read(),m=read(),ed=read();
    for(int i=1;i<=n;i++)scanf("%1d",&col[i]);
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),w=read();
        v1[x].push_back({y,w});
        v2[y].push_back({x,w});
        du[x]++;
    }
    for(int i=1;i<=n;i++)dis[i]=inf;
    dis[ed]=0,tot[ed]=1;
    q.push(ed);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(auto i:v2[u]){
            if(dis[i.to]>dis[u]+i.val){
                dis[i.to]=dis[u]+i.val;
                tot[i.to]=tot[u];
            }
            else if(dis[i.to]==dis[u]+i.val){
                tot[i.to]=(tot[i.to]+tot[u])%mod;
            }
            du[i.to]--;
            if(!du[i.to])q.push(i.to);
        }
    }
    for(int i=1;i<=n;i++){
        du[i]=v1[i].size();
    }
    q.push(ed);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(auto i:v2[u]){
            ll p=0;
            if(col[i.to]==false){
                p=inv(v1[i.to].size());
            }
            else if(dis[u]+i.val==dis[i.to]){
                p=(tot[u]*inv(tot[i.to]))%mod;
            }
            f[i.to]+=((f[u]+i.val)%mod*p)%mod;
            f[i.to]%=mod;
            du[i.to]--;
            if(!du[i.to])q.push(i.to);
        }
    }
    for(int i=1;i<=n;i++)printf("%lld ",f[i]);
    return 0;
}

M. Median Equals X

这个题在我和队友的努力下想不出来

posted @ 2026-10-01 21:31  TanHaoren  阅读(79)  评论(0)    收藏  举报