2026牛客寒假算法基础集训营6

K. 小L的游戏1

做法有很多,可以去二分一下两人总共至少需要操作多少次,使得总和大于等于 \(z\)

判断最后的次数的奇偶性即可

点击查看代码
#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,m,z;
    int x=0;

    cin>>n>>m>>z;

    int l=1,r=(z+m+n-1)/(n+m)*2+100;

    auto check=[&](int x)-> bool {
        int sum=0;
        sum+=(x+1)/2*n;
        sum+=(x)/2*m;
        return sum>=z;
    };

    while(l<r){
        int mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid+1;
    }

    if(l&1) cout<<0;
    else cout<<1;
    
}

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

    int ct=1;
    cin>>ct;
    while(ct--) solve();

    return 0;
}

H. 小L的数组

注意值域,不管怎么操作,最多会有 2048 个结果。所以对每一个位置,维护走到当前位置会有多少个结果即可

点击查看代码
#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;

    vector<int> a(n+1),b(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i]; 
    }

    vector<int> st(2048);
    st[0]=1;

    for(int i=1;i<=n;i++){
        vector<int> t(2048);
        for(int x=0;x<2048;x++){
            if(st[x]!=1) continue;
            t[max(0ll,x-a[i])]=1;
            t[x^b[i]]=1;
        }
        st=t;
    }

    for(int i=2047;i>=0;i--){
        if(st[i]){
            cout<<i;
            return;
        }
    }
}

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

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

G. 小L的散步

处理出所有缝隙的位置,再处理出所有脚步的位置(脚步位置由脚后跟和脚尖两个位置决定)

对每个脚步的位置,用 upper_bound 去判断一下,有没有一个缝隙恰好在这个脚步内部即可

点击查看代码
#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,m,l;
    cin>>n>>m>>l;

    vector<int> x(n+1),y(m+1);

    for(int i=1;i<=n;i++){
        cin>>x[i];
        x[i]+=x[i-1];
    }
    for(int i=1;i<=m;i++){
        cin>>y[i];
        y[i]+=y[i-1];
    }

    for(int i=1;i<=m;i++){
        int a=y[i-1],b=y[i];
        auto it=upper_bound(x.begin(),x.end(),a);
        if(it!=x.end()){
            //后脚的起点位置为a,终点为a+l
            //前脚:[b,b+l]
            auto val=*it;
            if((val>a && val<a+l)){
                cout<<"YES";
                return;
            }
        }

        it=upper_bound(x.begin(),x.end(),b);
        if(it!=x.end()){
            //后脚的起点位置为a,终点为a+l
            //前脚:[b,b+l]
            auto val=*it;
            if((val>b && val<b+l)){
                cout<<"YES";
                return;
            }
        }
    }

    cout<<"NO";
}

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

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

A. 小L的三角尺

关键点:

  1. 每次打磨的长度一定要是整数
  2. 最多打磨 \(1e6\)

所以可以每次只打磨 \(t=1\)

而对于所有的尺子,计算打磨掉 \(t=1\) 会减小多少长度,放入优先队列维护即可

点击查看代码
#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;

struct node{
    double x,y;
    double val;
    bool operator<(const node &t) const{
        return val<t.val;
    }
};

double cal(double a,double b){
    return sqrt(a*a+b*b);
}

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

    double sum=0;

    priority_queue<node> q;

    for(int i=1;i<=n;i++){
        double a,b;
        cin>>a>>b;
        sum+=cal(a,b);

        q.push({a,b,cal(a,b)-cal(a,b-1)});
    }

    double ans=0;

    for(int i=1;i<=w;i++){
        if(q.size()==0) continue;
        auto [a,b,val]=q.top();
        q.pop();

        ans+=val;

        if(b>1){
            q.push({a,b-1,cal(a,b-1)-cal(a,b-2)});
        }
    }

    printf("%.8lf",sum-ans);
}

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

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

B. 小L的彩球

两步,第一步是转化问题模型,第二步是组合数学

将左边的盒子看成 \(1\),右边的盒子看成 \(0\)

问题可以转化为:一个长度为 \(n\) 的字符串,由 \(x\)\(1\)\(n-x\)\(0\) 组成,且字符串中必须要有 \(t\)0110

将连续的 \(1\)\(0\) 压缩成一个,假设第一个位置是 \(0\),则:010101010... (长度为 \(t\)

则问题转化为:

\(x\)\(0\) 填入 \(\lceil t/2 \rceil\) 个位置,且每个位置最少填 \(1\) 个的填法

\(n-x\)\(1\) 填入 \(\lfloor t/2 \rfloor\) 个位置,且每个位置最少填 \(1\) 个的填法

此时,就是组合数学的隔板法。oiwiki 可搜

对于第一个位置是 \(1\) 的情况,同理。

两种情况结果相加即可

注意需要特判 \(t=0\) 的情况

点击查看代码
#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 = 998244353;

const int N=1e6;
vector<int> fact(N+2),infact(N+2);

int qmi(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}

void cal(){
    fact[0]=1,fact[1]=1;
    infact[0]=1,infact[1]=1;

    for(int i=2;i<=N;i++){
        fact[i]=i*fact[i-1]%mod;
    }
    infact[N]=qmi(fact[N],mod-2,mod);
    for(int i=N-1;i>=1;i--){
        infact[i]=infact[i+1]*(i+1)%mod;
    }
}

int C(int a,int b){
    if(a<0 || b<0 || a<b) return 0;
    return fact[a]*infact[b]%mod*infact[a-b]%mod;
}

void solve(){
    int n,x,t;
    cin>>n>>x>>t;

    if(t==0){
        //因为分成 0 段不符合隔板法都要求,所以不成立
        if(x==n) cout<<1<<endl;
        else cout<<0<<endl;
        return;
    }

    t++;

    int ans=C(x-1,(t+1)/2-1)*C(n-x-1,t/2-1)%mod;
    ans+=C(x-1,(t)/2-1)*C(n-x-1,(t+1)/2-1)%mod;

    cout<<ans%mod<<endl;
}

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

    cal();

    int ct=1;
    cin>>ct;
    while(ct--) solve();

    return 0;
}

D. 小L的扩展

本质还是 \(BFS\) 最短路,但是有一些点有时间限制。

所以可以把队列改成优先队列,这样可以保证,使用有限制的点时,已经到达被限制的时间。

具体看代码实现

点击查看代码
#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,m,a,b;
    cin>>n>>m>>a>>b;

    vector g(n+1,vector<int>(m+1));
    priority_queue<array<int,3>,vector<array<int,3>>,greater<array<int,3>>> q;
    for(int i=1;i<=a;i++){
        int x,y;
        cin>>x>>y;
        g[x][y]=-1;
        q.push({0,x,y});
    }

    vector lim(n+1,vector<int>(m+1));
    for(int i=1;i<=b;i++){
        int x,y,t;
        cin>>x>>y>>t;
        lim[x][y]=t;
    }

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

    int ans=0;
    while(q.size()){
        auto [time,x,y]=q.top();
        q.pop();

        for(int i=0;i<4;i++){
            int tx=dx[i]+x;
            int ty=dy[i]+y;

            if(tx<1 || tx>n || ty<1 || ty>m) continue;
            if(g[tx][ty]==-1) continue;
            int t=max(lim[tx][ty],time+1);
            g[tx][ty]=-1;
            q.push({t,tx,ty});
            ans=max(ans,t);
        }
    }

    cout<<ans<<endl;
}

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

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

E. 小L的空投

实现是简单的,主要是能不能想到思路。

如果正向考虑删除点和边,非常复杂。但是反向考虑,在图中增加边和节点则非常容易维护,使用并查集即可。

注意 unite 的过程中动态维护答案

点击查看代码
#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;
int n,m,x,d;

class DSU{
	public:
		vector<int> fa,sz;
		int setCount;
		int n;
        int ans;

        DSU(){}
        DSU(int n){
            init(n);
        }

		void init(int n){
            fa.resize(0);
            fa.resize(n+1);
            sz.resize(0);
            sz.resize(n+1,1);
            this->n=n;
            setCount=n;
  			iota(fa.begin(), fa.end(), 0);
            ans=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) return;

            int f1=(sz[x]>=d),f2=(sz[y]>=d);
            int f3=(sz[x]+sz[y]>=d);

            ans+=(f3-f1-f2);

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


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

    vector<pii> city(n+1);
    vector<int> mp(n+1);

    for(int i=1;i<=n;i++){
        cin>>city[i].first;
        mp[i]=city[i].first;
        city[i].second=i;
    }

    sort(city.begin()+1,city.end());
    reverse(city.begin()+1,city.end());

    vector<vector<int>> g(n+1);

    while(m--){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    vector<int> h(x+1);
    for(int i=1;i<=x;i++){
        cin>>h[i];
    }

    vector<int> ans(x+1);

    DSU dsu(n);

    int now=1;
    for(int i=x;i>=1;i--){
        while(now<=n && city[now].first>h[i]){

            int u=city[now].second;
            if(d==1) dsu.ans++;

            for(auto v:g[u]){
                if(mp[v]<=h[i]) continue;
                dsu.unite(u,v);
            }

            now++;
        }
        ans[i]=dsu.ans;
    }

    for(int i=1;i<=x;i++){
        cout<<ans[i]<<endl;
    }

}

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

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}
posted @ 2026-03-10 21:15  LYET  阅读(1)  评论(0)    收藏  举报