2025ccpc全国邀请赛(郑州)游记 + 前7题题解

(中间是我

赛前

6.2比赛,5.31前往郑州,正好赶上从游族实习离职。

早上七点多,背着大包小包前往上海南站,坐上了离开上海的高铁。

五个小时后,抵达郑州。

地铁非常方便,很快到学校附近,只是不知道地铁站到酒店居然有2km,推着一个大行李箱,和一个大羽毛球包,走了四十分钟才到酒店,折磨完了。

四点多到酒店,睡到了八点多,成功错过当晚abc。

十点多两个队友从保定坐高铁赶到。我跟他们说,楼下买一大桶水,这两天喝。然后他们买了三大桶,到最后还剩一桶5升的农夫山泉没喝完,拎到赛场又拎到了保定。

正好还能赶得上十点半的cf,成功上分。

十二点半cf结束后,都不困,遂继续补题,开始补之前vp的题目。

三点钟看了会欧冠决赛,这么多年巴黎终于是冠军了。

时间已经到了凌晨五点多,又一起商量了一下校集训队的规划,心情比较激动再加上睡了一下午,根本不困

于是6点多去楼下吃了豆腐脑+油条,上次吃甜豆腐脑好像还是很小的时候

将近七点才回酒店睡下,本来还想九点多去报道,一觉睡到了中午

两点多,罗老师到酒店后,才一起去赛场报到(今年发的衣服是真丑

下午是热身赛,出现了很多问题,卡评测,本地卡顿,等等一堆问题,但是第二天正式赛基本解决好了

当天晚上和罗老师聊了一会校集训队的规划,队友vp了会今年东北赛的题(不出意外的出意外了

而我呢

不得不准备6.3外教课考试的演讲稿

第二天早上不出意外没起来,还是罗老师打电话叫起来+买饭送到房间的

推着箱子到赛场,好像找不到比我行李更多的了

进入赛场,又坐到了xcpc的赛场中

赛时

前期推进还算顺利,大方向没问题,小错误一堆,每个题至少1个罚时,包括D签到题数开小了,E没开ll各wa了一发

赛时第一视角:

开局发现D是签到,于是wa1发后过D

队友发现J是签到,队友wa1发后过J

M好像比较简单,敲了个并查集上去很快过掉

看了会H题,发现好像是个诈骗题,直接输出r-l+1过掉

队友说会F了,写了个神秘二分上去,wa两发,至今不知道是怎么二分的(赛后高铁上又提出神秘分层图做法

看F,发现多源多终点01BFS即可,小细节tle一发后过掉

开G题构造题,发现奇数情况后,交给队友写,我去看E

队友思考半小时后成功得出一眼假的思路并wa一发,被我发现后狠狠压力

想出通解后让队友上机敲出代码,过掉G

开始看E题,感觉非常神秘,一开始有两个方向的思路,把组分出来,和不分组直接算

看榜过了一车人,感觉应该不是难题,但想半天想不出来

猜了一发根据奇偶位置分组,并用字典树维护

第一发wa了 彻底绝望,苦思冥想是哪里错了 此时队友提出是不是没开ll

果然define int ll后一发过掉

此时已经封榜,位于银牌区倒数

下一个BCK必须开一题,才能到银牌,最后B题三维优化概率DP,C题珂朵莉树维护区间,K题神秘卷积优化

实在是拼尽全力无法战胜

最终7题841罚时,遗憾铜首

赛后30块钱的比赛饭卡里还有不少钱,买了一堆吃的和饮料带了回去

罗老师还买了一堆咸鸭蛋

最后

深夜写下这篇流水账游记,这可能是大三老登最后一次比赛了,下半年网络赛想打出名额,靠一个人肯定是不可能的

只能期待学弟们,能提升实力到能和我一起多线程开题的水平,以及自己的实力能有提升

希望以后还能有机会参赛吧

真的还想打比赛

题解:

Problem D. 2025

签到题,第一发因为只循环到了500,wa了

点击查看代码
#include<iostream>
using namespace std;

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

    int t=n;

    int val=0;
    while(t){
        val+=t%10;
        t/=10;
    }

    bool f1=0,f2=0;

    for(int i=1;i<=1000;i++){
        if(i*i==n) f1=1;
        if(i*i==val) f2=1;
    }

    if(f1 && f2){
        cout<<"Yes";
    }else{
        cout<<"No";
    }
}

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

    int ct=1;
    // cin>>ct;
    while(ct--){
        solve();
    }
}
/**********************************************************************
	Problem: 1009
	User: D1003
	Language: C++
	Result: AC
	Time:2 ms
	Memory:2304 kb
**********************************************************************/

Problem J. Ring Trick

签到题,没看题,队友过的

点击查看代码

#include <bits/stdc++.h>

int main()
{
    std::ios::sync_with_stdio(false),std::cin.tie(nullptr);

    auto s = std::string{};
    auto constexpr hole = std::array {
        1,2,0,1,0,0,0,0,0,0,0,0,0,
        0,1,1,1,1,0,0,0,0,0,0,0,0
    };

    std::cin >> s;
    auto n  = int(s.size());
    auto ans = 0;
    for(auto k = 0; k != 26; ++k) {
        auto cnt = 0;
        for(auto i = 0; i != n; ++i) {
            cnt += hole[(s[i] - 'A' + k) % 26];
        }
        ans = std::max(ans,cnt);
    }
    std::cout << ans << '\n';

}
/**********************************************************************
	Problem: 1015
	User: D1003
	Language: C++
	Result: AC
	Time:33 ms
	Memory:3876 kb
**********************************************************************/

Problem M. 川陀航空学院

首先,对于每一个节点个数为 t 的连通块,把边的数量删到 t-1

然后,再把 x 个不用的联通块用 x-1 条边连起来即可,全程可以用并查集维护

点击查看代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<numeric>
#include<map>
using namespace std;

int cnt;

class dsu{
    public:
    vector<int> fa,sz,edge;
    int setCount;
    int n;


    dsu(){};
    dsu(int n){
        fa.assign(n+1,0);
        sz.assign(n+1,1);
        edge.assign(n+1,0);
        this->n=n;
        setCount=n;
        iota(fa.begin(),fa.end(),0);
    }

    int find(int x){
        if(fa[x]==x) return x;
        return fa[x]=find(fa[x]);
    }

    void unite(int x,int y){
        x=find(x),y=find(y);
        if(x==y){
            cnt++;
            return;
        }

        if(sz[x]<=sz[y]) swap(x,y);
        fa[y]=fa[x];
        sz[x]+=sz[y];
        --setCount;
        edge[x]++;
        edge[x]+=edge[y];
    }
}; 

void solve(){
    int n,m;
    cin>>n>>m;
    dsu ds(n);
    // int cnt=0;

    while(m--){
        int u,v;
        cin>>u>>v;
        ds.unite(u,v);
    }

    for(int i=1;i<=n;i++){
        if(ds.find(i)!=i) continue;
        cnt+=ds.edge[i]-(ds.sz[i]-1);
    }

    cnt+=ds.setCount-1;
    cout<<cnt;
}

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

    int ct=1;
    // cin>>ct;
    while(ct--){
        solve();
    }
}
/**********************************************************************
	Problem: 1018
	User: D1003
	Language: C++
	Result: AC
	Time:188 ms
	Memory:14028 kb
**********************************************************************/

Problem H. 树论函数

打表发现,n 和 n+1 联通,又因为范围是无穷,且赛时过了一车人,guess了一发都联通,ac

具体的证明可以看gym上的官方题解,写的很清楚,大概就是 n 和 n+1 一定和更大的一个点 x 联通

点击查看代码
#include<iostream>
using namespace std;

void solve(){
    int s,l,r;
    cin>>s>>l>>r;
    cout<<r-l+1<<endl;
}

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

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }
}
/**********************************************************************
	Problem: 1013
	User: D1003
	Language: C++
	Result: AC
	Time:313 ms
	Memory:2304 kb
**********************************************************************/

Problem F. 幻形之路

先特判一下能不能直接走到,如果可以直接输出0即可

把 (1,1)能走到的所有障碍物,设置成源点,放进队列,把(n,m)能走到的所有非障碍物,设置成终点。

因为边权是1,所以BFS即可

点击查看代码
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<vector>
#define endl '\n';
using namespace std;
using pii=pair<int,int>;
const int N=1010;
const int inf=1e9;
char g[N][N];
int st[N][N];
int dist[N][N];
int n,m;
queue<pii> q;

int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};

void bfs1(int x,int y,int mk){
    st[x][y]=1;
    for(int i=0;i<4;i++){
        int a=x+dx[i],b=y+dy[i];
        if(a>=1 && a<=n && b>=1 && b<=m && !st[a][b]){
            if(g[a][b]=='.') bfs1(a,b,mk);
            else if(mk==0){
                q.push({a,b});
                dist[a][b]=1;
                st[a][b]=1;
            }
        } 
    }
}


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

    while(q.size()) q.pop();

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>g[i][j];
            st[i][j]=0;
            dist[i][j]=inf;
        }
    }

    bfs1(1,1,0);

    if(st[n][m]){
        cout<<0<<endl;
        return;
    }
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            st[i][j]=0;
        }
    }

    bfs1(n,m,1);
    int ans=n+m-2;

    while(q.size()){
        auto [x,y]=q.front();
        q.pop();

        // cout<<"i: "<<x<<" j: "<<y<<" dist: "<<dist[x][y]<<endl;

        for(int i=0;i<4;i++){
            int a=x+dx[i],b=y+dy[i];
            if(a>=1 && a<=n && b>=1 && b<=m){
                
                if(st[a][b]==0 && dist[x][y]+1<dist[a][b]){
                    dist[a][b]=dist[x][y]+1;
                    q.push({a,b});
                }
                else if(st[a][b]==1 && dist[x][y]<dist[a][b]){
                    dist[a][b]=dist[x][y];
                    ans=min(ans,dist[a][b]);
                }
            }
        }
    }

    cout<<ans<<endl;

}

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

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }
}
/**********************************************************************
	Problem: 1011
	User: D1003
	Language: C++
	Result: AC
	Time:53 ms
	Memory:104900 kb
**********************************************************************/

Problem G. 直径与最大独立集

特判一下2和4

然后奇数偶数分别如下构造

点击查看代码

#include <bits/stdc++.h>

void solve()
{
    int n;
    std::cin >> n;
    if(n == 2) {
        std::cout << 1 << ' ' << 2 << '\n';
        return;
    }
    if(n == 4) {
        std::cout << -1 << '\n';
        return;
    }
    auto d = (n / 2) + 1; // zhi jing

    auto now = (d / 2) + 1; // xian zai you de

    auto have = n - d - 1; // bu de dian
    auto even = d - now;
    auto odd = have - even;
    auto tot = d + 2;
    for(auto i = 1; i < tot - 1; i += 1) {
        std::cout << i << ' ' << i + 1 << '\n';
    }

    auto it = 2;

    while(have) {
        std::cout << it++ << ' ' << tot++ << '\n';
        --have;
    }
 
    // std::cout << std::format("even = {},odd = {}\n",de,doo);

}

int main()
{
    std::ios::sync_with_stdio(false),std::cin.tie(nullptr);

    int t;
    std::cin >> t;
    while(t--) {
        solve();
    }

}
/**********************************************************************
	Problem: 1012
	User: D1003
	Language: C++
	Result: AC
	Time:8 ms
	Memory:2304 kb
**********************************************************************/

Problem E. 双生魔咒

结论是排序后根据奇偶位置分组

说下赛时感性的推测

对每个字符串 i ,肯定要尽量把前缀最匹配的字符串 j 放到另一个组

而按排序奇偶位置分组,刚好满足这个要求

同时赛时过了一车人,感觉不是难题

分组后计算答案时,把其中一组放进字典树中即可

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N=2e5+10;
int tr[N][26];
int idx;
int cnt[N*26];
int ans;

void insert(string &s){
    int now=0;
    for(int i=0;i<s.size();i++){
        int ch=s[i]-'a';
        if(tr[now][ch]==0){
            tr[now][ch]=++idx;
        }
        now=tr[now][ch];
        cnt[now]++;
    } 
}

void find(string &s){
    int now=0;
    for(int i=0;i<s.size();i++){
        int ch=s[i]-'a';
        if(tr[now][ch]==0){
            return;
        }
        now=tr[now][ch];
        ans+=cnt[now];
    }
}

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

    vector<string> a(2*n+1);

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

    sort(a.begin()+1,a.end());

    for(int i=1;i<=2*n;i++){
        if(i&1) insert(a[i]);
    }

    for(int i=1;i<=2*n;i++){
        if(!(i&1)) find(a[i]);
    }

    cout<<ans;



}

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

    int ct=1;
    // cin>>ct;
    while(ct--){
        solve();
    }
}
/**********************************************************************
	Problem: 1010
	User: D1003
	Language: C++
	Result: AC
	Time:26 ms
	Memory:89820 kb
**********************************************************************/
posted @ 2025-06-04 02:35  LYET  阅读(896)  评论(2)    收藏  举报