Typesetting math: 100%
为了能到远方,脚|

园龄:粉丝:关注:

2025牛客寒假算法基础集训营3

M

检验字符的字符是否能构成nowcoder
两种方法

  1. 检验各个字母出现的次数是否与nowcoder相等

  2. 按照字典序排序后是否与cdenoorw相同

#include <bits/stdc++.h>
using namespace std;
string s;
int main() {
    cin>>s;
    sort(s.begin(),s.end());
    if(s=="cdenoorw")
    {
        cout<<"happy new year"<<endl;
    }else{
        cout<<"I AK IOI"<<endl;
    }
}

A

与偶数互质的数一定是奇数

奇数-奇数=偶数
偶数-奇数=奇数

所以,当数为1时,即数为奇数时胜利
假设先手拿着奇数,那么必然变为偶数,而后手拿着偶数,也必然变为奇数,所以显然,先手胜
反过,必输

总结一下,一开始就确定了,先后手上的数的特征:奇数or偶数
而赢家一定是奇数

Ps:注意数据范围

#include<bits/stdc++.h>

using namespace std;
long long n;
int main(){
    cin>>n;
    if(n&1) puts("Yes");
    else puts("No");
    return 0;
}

F

如果每个人中间没有人,那么看见的人数会达到最大n*2
如果每个人只有中间有人,那么看见的人数最小:n
不在这个范围内的无法构造

#include<bits/stdc++.h>

using namespace std;
#define ull unsigned long long 

int t,a,b,c,n;
void solve(){
    cin>>n>>a>>b>>c;
    int x,y,z;
    if(a+b+c>(2*n) || a+b+c<n){
        puts("No");
    }
    else puts("Yes");
}
int main(){
    cin>>t;
    while(t--){
        solve();
    }
    
    return 0;
}

L

N7
solutions:

  1. 有毅力的可以手写所有答案
  2. 建图,求欧拉序列
  • code
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
int n;

int a[100][100];
const int maxn=2e5+10;
struct node{
    int v,next;
}e[maxn];
int head[maxn];
int cnt=0;
map<pair<int,int>,bool >mp;
void add(int u,int v){
    e[++cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}
vector<int>ans;

bool check=0;
void dfs(int u){
    if(u==1 && ans.size()==3*(1+n)*n/2+1){
        puts("Yes");
        for(int i=0;i<=3*(1+n)*n/2;++i) cout<<ans[i]<<" ";
        check=1;
        return ;
    }
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(mp[{u,v}] || mp[{v,u}]) continue;
        mp[{v,u}]=mp[{u,v}]=1;
        ans.push_back(v);
        dfs(v);
        if(check) return ;
        ans.pop_back();
        mp[{v,u}]=mp[{u,v}]=0;    
    }
}

int main(){
    cin>>n;
    ;int res=0;n+=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=i;++j)
            a[i][j]=++res;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=i;++j){
            if(j!=i) {
            	add(a[i][j],a[i][j+1]),add(a[i][j+1],a[i][j]);
			}
            if(i!=n){
	            add(a[i][j],a[i+1][j]);
	            add(a[i][j],a[i+1][j+1]);
	            add(a[i+1][j],a[i][j]);
	            add(a[i+1][j+1],a[i][j]);
			}
        }
        n-=1;
    ans.push_back(1);
    dfs(1);
    if(!check) puts("No");
    return 0;
}

E

对于每一次碰撞,都会交换速度,而那么对于以后的碰撞相当于 小球相互穿过,问穿过了多少次

思考:

  • 如果时间无限长,那么穿过次数一定会达到一个上限

  • 刚开始,穿过次数为0

    所以总得来说,穿过次数随着时间增长而增长

答案那么具有单调性,就可以考虑二分答案

  • 答案得范围容易确定[0,109]

  • 如何检验答案
    根据我们小学二年级学的,速度是相对的,我们可以这么规定向左的速度为0,那么向右的速度为2
    此时我们可以一个一个计算向右的通过了多少个静止不动的

    • 例如,现在当前的向右的位置为p,在当前时间t下,到达了p+2t,因此只要在[p,p+2t]内静止不动的都能被计算在内

    这下问题就转化成如何高效统计这个区间内静止的球:

    我们可以将静止的球排序,通过双指针,界定范围

#include<bits/stdc++.h>

using namespace std;
#define p first
#define v second
const int maxn=1e5+10;
typedef pair<int,int> pii;

int n,k;
vector<int>a,b;
bool check(double x){
    int cnt=0;
    int l=0,r=0;
    int len=b.size();
    for(auto i:a){
        while(l<len && b[l]<i) ++l;
        while(r<len && b[r]<=i+2*x) ++r;
        cnt+=r-l;
    }
    return cnt>=k;
}
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;++i){
        int p,v;
        cin>>p>>v;
        if(v==1)        a.push_back(p);
        else b.push_back(p);
    }    
    sort(a.begin(),a.end());
    sort(b.begin(),b.end());
    double l=0,r=1e9+10;
    while((r-l)>1e-6){
        double mid=(l+r)/2;
        if(check(mid))          r=mid-1e-6;
        else l=mid+1e-6;
    }
    if(r==1e9+10) puts("No");
    else {
        puts("Yes");
        cout<<fixed<<setprecision(6)<<l<<"\n";
    }
}

G

先要学习数论分块
然后
在数论分块中,当k[l,r]时,存在整数m=Nk保持恒定。此时余数N%k的变化规律可描述为:

N%k=Nmk

等差特性证明

公差推导:

k递增1时:

Δ(N%k)=[Nm(k+1)][Nmk]=m

因此余数构成公差为m的等差数列

参数对应关系:
  • 首项:a0=N%l=Nml
  • 末项:arl=N%r=Nmr
  • 项数:rl+1

N%i在每一个分块区间可表示为等差数列ai=a0+(i1)×b,其中a0=N%l,b=Nl

所以i=aia0b+1

无论是通过这个公式还是直接想,一个块内余数都是单调递减的,于是我们可以二分查找第k大的余数

假设前k大的余数是mid,所以每个块前大于mid的数目=mida0b+1

确定mid之后,再将每个块内大于mid的值全部相加

#include<bits/stdc++.h>

using namespace std;
int n,k;
const int maxn=1e9+10;

int main(){
    cin.tie(0);
    cout.tie(0);
    cin>>n>>k;
    int l=1, r=n;
    int val=0, vtot=0;
    while(l<=r) {
        int mid=(l+r)>>1;
        int tot=0;
        for(int i=1,j; i<=n; i=j+1) {
            j = n/(n/i);        // 数论分块确定区间 [i, j]
            int a = n - (n/i)*i; // 余数 a = n % i
            int k = n/i;         // 商 k = ⌊n/i⌋
            if(a < mid) continue;
            // 计算余数 ≥ mid 的数的个数
            int cnt = min((a - mid)/k + 1, j - i + 1);
            tot += cnt;
        }
        if(tot >= k) l = mid+1; // 余数 ≥ mid 的个数足够多,尝试更大的 mid
        else {
            vtot = tot;         // 记录当前 tot
            val = mid;          // 记录候选 val
            r = mid-1;          // 尝试更小的 mid
        }
    }
    long long ans = 1ll*(k - vtot)*(val-1);
    for(int i=1,j; i<=n; i=j+1) {
        j = n/(n/i);
        int a = n - (n/i)*i;    // 余数 a = n % i
        int k = n/i;            // 商 k = ⌊n/i⌋
        if(a < val) continue;
        // 计算余数 ≥ val 的数的余数之和
        int len = min((a - val)/k + 1, j - i + 1);
        ans += 1ll*(a*2 - k*(len-1)) * len / 2;//等差数列求和公式
    }
    cout<<ans<<endl;
    return 0;
}

本文作者:归游

本文链接:https://www.cnblogs.com/guiyou/p/18695858

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   归游  阅读(14)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起