2025 牛客寒假算法基础集训营 2 个人题解

A 一起奏响历史之音!

签到,判断 a>=1 and a<=6 and a!=4 即可。

B 能去你家蹭口饭吃吗

还是签到,答案为 $sorted(a)_{\lfloor{n\over2}\rfloor}-1$。

D 字符串里串

正反跑一边,只有当前字母 $si$ / $s{n-i}$ 在后面 / 前面还有出现,答案就可以更新为 $\max(i,ans)$。

F 一起找神秘的数!

首先理解

a+b=2(a\And b)+(a \oplus b)

$2(a\And b)$ 是只进位二进制加法,$a \oplus b$ 是不进位二进制加法

将上面公式代入题目,得到

a|b=a\And b

显然解只有 $a=b$。答案为 $r-l+1$。

G 一起铸最好的剑!

找到距离 $n$ 最小的两个 $m^x$ ,使之更接近的 $x$ 就是答案。

罚时吃满,这题要求一定要启动炉子,答案最小为 1。所以特判 n==1 or m==1 : ans=1

H 一起画很大的圆!

外接圆半径

R={abc \over4S}

在长边的最左 / 右选一个长度为 1 的线段为三角形一边,剩下部分与短边与长边相邻的长度 1 线段,构成的斜边为三角形第二条边。至此三角形已经确定。

横着可以找到一个答案是 $(a,d−1),(b,d),(b−1,d)$,但如果 $d−c>b−a$ 的话,就应该竖着找。

void solve(){
    int a,b,c,d;
    cin>>a>>b>>c>>d;
    if (b-a>d-c){
        cout<<a<<" "<<d-1<<"\n";
        cout<<b-1<<" "<<d<<"\n";
        cout<<b<<" "<<d<<"\n";
    }else{
        cout<<a+1<<" "<<c<<"\n";
        cout<<a<<" "<<d-1<<"\n";
        cout<<a<<" "<<d<<"\n";
    }
}

J 数据时间?

Python 秒

from collections import defaultdict
from datetime import datetime
strptime=datetime.strptime
ri=lambda : int(input())
rl=lambda : list(map(int,input().split()))
def solve():
    n,h,m=rl()
    res=[set() for i in range(3)]
    for i in range(n):
        a=input().split()
        d=strptime(a[1]+" "+a[2],f"%Y-%m-%d %H:%M:%S")
        if d.year==h and d.month==m:
            if d.hour>=7 and d.hour<=8 or d.hour==9 and d.minute==0 and d.second==0:
                res[0].add(a[0])
            elif d.hour>=18 and d.hour<=19 or d.hour==20 and d.minute==0 and d.second==0:
                res[0].add(a[0])
            elif d.hour>=11 and d.hour<=12 or d.hour==13 and d.minute==0 and d.second==0:
                res[1].add(a[0])
            elif d.hour>=22 or d.hour==0 or d.hour==1 and d.minute==0 and d.second==0:
                res[2].add(a[0])
    print(len(res[0]),len(res[1]),len(res[2]))

K 可以分开吗?

DFS 板子题之一,遍历所有连通块,找最少的相邻灰块。

#include<bits/stdc++.h>
#define up(i,x,y) for(int i=x;i<=y;i++)
#define down(i,x,y) for(int i=x;i>=y;i--)
#define elif else if
#define ll long long
using namespace std;
vector<vector<int>>vis;
vector<array<int,2>>path;
vector<string>tu;
vector<array<int,2>>dt{{-1,0},{0,-1},{0,1},{1,0}};
int n,m;
void dfs(int y,int x){
    path.push_back({y,x});
    vis[y][x]=1;
    for (auto&&[dy,dx]:dt){
        int gy=y+dy,gx=x+dx;
        if (gy>n or gy<1 or gx>m or gx<1 or tu[gy][gx]=='0' or vis[gy][gx]) continue;
        dfs(gy,gx);
    }
}
void solve(){
    cin>>n>>m;
    vis=vector(n+2,vector(m+2,0));
    tu=vector<string>(n+1);
    int mxc=INT_MAX;
    up(i,1,n) {
        cin>>tu[i];
        tu[i]="^"+tu[i];
    }
    up(i,1,n){
        up(j,1,m){
            if (not vis[i][j] and tu[i][j]=='1'){
                path.resize(0);
                dfs(i,j);
                int cnt=0,flg=i*m+j;
                for (auto&&[y,x]:path){
                    for (auto&&[dy,dx]:dt){
                        int gy=y+dy,gx=x+dx;
                        if (gy>n or gy<1 or gx>m or gx<1 or tu[gy][gx]=='1' or vis[gy][gx]==flg) continue;
                        vis[gy][gx]=flg;
                        cnt++;
                    }
                }
                mxc=min(mxc,cnt);
            }
        }
    }
    cout<<mxc<<"\n";
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    // cin>>T;
    while(T--)solve();
}

C 字符串外串

构造题。

形如 “cbaaaabc” 的构造方法,构造一个长度为 $n$ 的字符串,最后的 $m$ 应该等于 $n$ 减去最后 “abc..z” 的长度。显然 n==m 时无解,n-m > 26 or n-m > m+1 时这种方法无解。

形如 “cbadefgabc” 的构造方法,构造一个长度为 $n$ 的字符串,最后的 $m$ 应该等于最后 “abc..z” 的长度。显然 n-2*m>26-m 的时候这种方法无解。

相结合得到本题答案。

#include<bits/stdc++.h>
#define up(i,x,y) for(auto i=x;i<=y;i++)
#define down(i,x,y) for(auto i=x;i>=y;i--)
#define elif else if
#define ll long long
using namespace std;
void solve(){
    int n,m;
    cin>>n>>m;
    if (n<=m or n-m>26 or (n-m>m+1)){
        if (n-2*m>=0 and n-2*m<=26-m){
            cout<<"YES\n";
            up(i,'a',char('a'+m-1)){
                cout<<i;
            }
            up(i,char('a'+m),char('a'+m+(n-m*2)-1)) cout<<i;
            down(i,char('a'+m-1),'a'){
                cout<<i;
            }cout<<"\n";
            return;
        }
        cout<<"NO\n";
        return;
    }
    cout<<"YES\n";
    {
        int gap=n-m;
        char cur='a'+gap-1;
        while(cur>='a'){
            cout<<cur;
            cur--;
        }
        up(i,1,n-gap*2){
            cout<<"a";
        }
        up(i,int(n-m==m+1),gap-1) cout<<char('a'+i);
    }
    cout<<"\n";
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    cin>>T;
    while(T--)solve();
}

E 一起走很长的路!

蒟蒻作者的解法(比 std 快!)

贪心的想,增加重量一定不会比减少重量更劣,把重量增加给 $a_l$ 一定不会比后面更劣。

设 $cur$ 为当前增加的重量。

当我们确定 $a_{i}$($i\not=l$)可行,想要确定接下来的点可不可行,由于 $ai$ 可行的条件是 $pre{i-1}-pre_{l-1}+cur\ge a_i$,我们可以确定现在的和一定 $\ge 2a_i$,也就是说我们每次只需要判断下一个大于 $2a_i$ 的 $a_j$ 符不符合条件就行,相当于会一直倍增判断值。显然到一定值以后,后面就不会有更大的值,查询就结束了。这样每次查询时间复杂度就是 $O(\log V)$,题中 $V=10^9$,可以通过这道题。

初始时 $a_l$ 显然一定可以,设置要判断的点 $p=l+1$,运行到 $p>r$ 结束。

怎么找到下一个大于 $2a_i$ 的值?预处理,使用离散化 + 树状数组,倒着跑一遍,记录下一个大于 $a_i$ 的值的下标。预处理时间复杂度 $O(n\log n)$(我的代码是大于等于,导致查询常数稍微大一点)。

整体复杂度 $O(n\log n+q\log V)$。

#include<bits/stdc++.h>
#define up(i,x,y) for(auto i=x;i<=y;i++)
#define down(i,x,y) for(auto i=x;i>=y;i--)
#define elif else if
#define ll long long
using namespace std;
ll nn;ll n,q;
vector<ll>t;
void assign(ll p,ll x){
    while(p){
        t[p]=min(t[p],x);
        p-=(p&(-p));
    } 
}
ll query(ll p){
    ll res=n+1;
    while(p<=nn){
        res=min(res,t[p]);
        p+=(p&(-p));
    }
    return res;
}
void solve(){
    cin>>n>>q;
    vector<ll>a(n+1),pre(n+1);
    pre[0]=0;
    vector<ll>nxt(n+1,n+1);
    vector<array<ll,2>>b(n+1);
    up(i,1,n){
        cin>>a[i];
        pre[i]=pre[i-1]+a[i];
        b[i]={a[i],i};
    }
    sort(b.begin(),b.end());
    vector<ll>rev(1,0);
    vector<ll>get(n+1);
    {
        ll p=0,last=0;
        for (auto&&[x,i]:b){
            if (x!=last){
                last=x;
                p++;
                rev.push_back(x);
            }
            get[i]=p;
        }nn=p;
    }
    t=vector(nn+1,n+1);
    down(i,n,1){
        int pos=lower_bound(rev.begin(),rev.end(),a[i]*2)-rev.begin();
        nxt[i]=query(pos);
        assign(get[i],i);
    }
    while(q--){
        ll u,v;
        cin>>u>>v;
        if (u==v){
            cout<<"0\n";
            continue;
        }
        ll res=0;
        ll p=u+1;
        while(p<=v){
            if (a[p]>pre[p-1]-pre[u-1]+res){
                res+=a[p]-(pre[p-1]-pre[u-1]+res);
            }
            p=nxt[p];
        }
        cout<<res<<"\n";
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    // cin>>T;
    while(T--)solve();
}

标程

我们在上面说过,贪心的想,增加重量一定不会比减少重量更劣,把重量增加给 $a_l$ 一定不会比后面更劣。

显然,答案就是

\max_{i=l+1}^{r}(a_i-(pre_{i-1}-pre_{l-1}))

如果暴力求 $\max$,时间复杂度就是 $O(n)$。

我们发现(作者考试时没发现 qwq),对于每次查询,$pre{l-1}$ 是固定的,所以我们求的其实是

\max_{i=l+1}^{r}(a_i-pre_{i-1})+pre_{l-1}

使用 ST 表等处理 RMQ 即可。

作者还没写,先用 王嘤嘤 的代码顶上。

#include<bits/stdc++.h>
using namespace std;
template <typename T>
class ST{
public:
    const int n;
    vector<vector<T>> st;
    ST(int n = 0, vector<T> &a = {}) : n(n){
        st = vector(n + 1, vector<T>(22 + 1));
        build(n, a);
    }
    inline T get(const T &x, const T &y){
        return max(x, y);
    }
    void build(int n, vector<T> &a){
        for(int i = 1; i <= n; i++){
            st[i][0] = a[i];
        }
        for(int j = 1, t = 2; t <= n; j++, t <<= 1){
            for(int i = 1; i <= n; i++){
                if(i + t - 1 > n) break;
                st[i][j] = get(st[i][j - 1], st[i + (t >> 1)][j - 1]);
            }
        }
    }
    inline T find(int l, int r){
        int t = log(r - l + 1) / log(2);
        return get(st[l][t], st[r - (1 << t) + 1][t]);
    }
};
int main(){
    int n, q;
    cin >> n >> q;
    vector f(n + 1, 0ll), d = f;
    for(int i = 1; i <= n; i++){
        int x;
        cin >> x;
        f[i] = f[i - 1] + x;
        d[i] = x - f[i - 1];
    }
    ST<long long> st(n, d);
    while(q--){
        int l, r;
        cin >> l >> r;
        if(l == r){
            cout << 0 << endl;
            continue;
        }
        auto ma = st.find(l + 1, r);
        auto ans = max(ma + f[l - 1], 0ll);
        cout << ans << endl;
    }
}