A. 字符画

暴力

大概思路

倒着扫描一遍整个图就可以排除遇到假8,假P,假F的可能了

参考代码

string P[6],B[6],F[6];
string s[505];
int cP,cB,cF;

void EX(int cx,int cy){//擦除已匹配图块
    FOR(i,cy-2,cy+2){
        s[i]=(s[i].substr(0,cx-1)+"...")+s[i].substr(cx+1+1);
    }
}

bool ck(char c,int cy,int cx){//检查是否为某个图块
    if(c=='B'){
        FOR(i,cy-2,cy+2){
            if(s[i].substr(cx-1,3)!=B[i-(cy-2)+1])return 0;
        }
        cB++;
        EX(cx,cy);
        return 1;
    }else if(c=='P'){
        FOR(i,cy-2,cy+2){
            if(s[i].substr(cx-1,3)!=P[i-(cy-2)+1])return 0;
        }
        cP++;
        EX(cx,cy);
        return 1;
    }else{
        FOR(i,cy-2,cy+2){
            if(s[i].substr(cx-1,3)!=F[i-(cy-2)+1])return 0;
        }
        cF++;
        EX(cx,cy);
        return 1;
    }
}

void solve(){
    B[1]="###";
    B[2]="#.#";
    B[3]="###";
    B[4]="#.#";
    B[5]="###";
    
    P[1]="###";
    P[2]="#.#";
    P[3]="###";
    P[4]="#..";
    P[5]="#..";
    
    F[1]="###";
    F[2]="..#";
    F[3]=".##";
    F[4]="..#";
    F[5]="..#";
    
    CIN(n);CIN(m);

    FOR(i,1,n){cin>>s[i];s[i]=" "+s[i];}
    rFOR(i,n-2,1+2)FOR(j,1+1,m-1){//倒序遍历整个图
        for(auto c:{'B','P','F'}){
            if(ck(c,i,j))break;
        }
    }
    cout<<cP<<" "<<cF<<" "<<cB;
}

G. 能赢吗?会赢的!

二分。(思路非常快的一题,第一次抢到线下赛首AC)

大概思路

一眼二分,ck函数按照题意模拟即可。

参考代码

void solve(){
    CIN(n);vector<int> a(n+1,0),h(n+1,0);
    FOR(i,1,n){cin>>a[i];h[i]=a[i]+(a[i-1]+1)/2;}//读入“基础生命值”后预处理“真实生命值”(其实没必要,在ck函数里把h[i]换成a[i]+(a[i-1]+1)/2也行,这里预处理只是略微降低了一下常数(加减乘除的常数有降低的必要嘛))
    auto ck=[&](ll x){//二分ck函数,按题意模拟即可
        FOR(i,1,n){
            if(x>h[i]){
                x+=a[i]/2;
            }else{
                return 0;
            }
        }
        return 1;
    };
    ll l=0,r=1e18;
    while(l<=r){//二分板子,注意边界
        ll mid=(l+r)/2;
        if(ck(mid))r=mid-1;
        else l=mid+1;
    }
    cout<<r+1;//这里要注意,一般是看你ck(mid)成功后,靠r/l更新的式子反推回去
}

H. NING-NING-chatgpt

水题,map

大概思路

直接map映射即可(赛时还把映射写反了,一个个对换回去。。)

参考代码

void solve(){
    map<char,char> mp;
    FOR(i,'a','z')mp[i]=i;
    mp['4']='a';mp['6']='b';
    mp['3']='e';mp['9']='g';
    mp['1']='l';mp['0']='o';
    mp['5']='s';mp['7']='t';
    mp['2']='z';mp[' ']=' ';
    string s;getline(cin,s);//有强调空格的题一律用getline
    for(auto c:s)cout<<mp[c];
}

J. 矩阵最大路径与

位运算,数位DP,BFS

大概思路

按数位倒着跑BFS,逐个确定ans的各个数位的最优状态

参考代码

void solve(){
    CIN(n);CIN(m);
    vector<vector<int>> a(n+1,vector<int>(m+1));
    vector<vector<int>> vis(n+1,vector<int>(m+1));
    FOR(i,1,n)FOR(j,1,m)cin>>a[i][j];
    
    ll ans=0;
    rFOR(k,29,0){
        FOR(i,1,n)FOR(j,1,m)vis[i][j]=0;
        ans|=(1<<k);//设当前位为1,试试跑到最后能不能保留这位
        queue<pair<int,int>> q;
        if(((a[1][1]&ans)==ans)&&((a[n][m]&ans)==ans)){
            q.push({1,1});
            vis[1][1];
        }
        int dx[]={0,0,1,-1};//一种很新的四方向遍历写法
        int dy[]={1,-1,0,0};
        while(q.size()){
            auto t=q.front();q.pop();
            auto &[x,y]=t;
            FOR(i,0,3){
                int tx=x+dx[i];
                int ty=y+dy[i];
                if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&(vis[tx][ty]==0)){
                    if((a[tx][ty]&ans)==ans){
                        vis[tx][ty]=1;
                        q.push({tx,ty});
                    }else{
                        vis[tx][ty]=2;//vis设置成不为0就行,代表已经走过了
                    }
                }
            }
        }
        if(vis[n][m]!=1)ans^=(1<<k);//如果没法保留,那么撤销这位的置1
    }
    cout<<ans;
}

K. gzhu is all you need!

模拟

大致思路

按题意跑模拟即可

参考代码

void solve(){
    CIN(n);string s;cin>>s;
    map<char,int> mp;
    ll power=0,cost=0;
    for(auto c:s){
        if(power)power--;
        else{
            cost++;
            mp[c]++;
            if(mp['g']&&mp['z']&&mp['h']&&mp['u']){
                power+=mp['g']+mp['z']+mp['h']+mp['u'];
                mp['g']=mp['z']=mp['h']=mp['u']=0;
            }
        }
    }
    cout<<cost;
}

M. 计算几何

排序

大概思路

根本就不是计算几何,是线段相交而不是直线相交,直接按点排序,一个个连过去即可

参考代码

void solve(){
    CIN(n);
    vector<tuple<int,int,int>> a;
    FOR(i,1,n){
        CIN(x);CIN(y);
        a.push_back({x,y,i});
    }
    sort(a.begin(),a.end());
    cout<<n/2<<ET;
    FOR(i,0,(n>>1<<1)-1){
        cout<<get<2>(a[i])<<" \n"[i%2];
    }
}

附录

这里贴上用到的头部和main函数:

#include<iostream>
#include<vector>
#include<cmath>
#include<set>
#include<bits/stdc++.h>
#define ll long long
#define ld long double
using namespace std;
#define ET '\n'
#define FOR(i,a,b) for(ll i=(a);i<=(b);i++)
#define rFOR(i,a,b) for(ll i=(a);i>=(b);i--)
//趋向for循环,一般情况别用,规定方向时会跳出的循环在这里不会跳出
#define rep(i,a,b) for(ll i=(a);i!=(b)+2*(a<b)-1;i+=2*(a<b)-1)
#define CIN(a) ll a;cin>>a
#define DCIN(a) ld a;cin>>a
#define SCIN(a) string a;cin>>a
#define CA cout<<ans<<ET
#define CY cout<<"YES"<<ET
#define CN cout<<"NO"<<ET
#define max(a,b) ((a>b)?(a):(b))
#define min(a,b) ((a<b)?(a):(b))
std::mt19937 myrand(time(0));
ll rnd(ll l, ll r) {return std::uniform_int_distribution<ll>(l, r)(myrand);}
//没有C++20就不能用ranges了//CF又可以用C++20了,封印解除
#define PP(l,r,CK) *ranges::partition_point(ranges::iota_view((l),(r)+1),(CK))
int M=1e9+7;
inline ll MO(ll x){return (x%M+M)%M;}
string ANS[2]={"No\n","Yes\n"};

void solve(){

}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //pre();
    //CIN(t);while(t--)
    solve();
    return 0;
}