2021牛客寒假算法基础集训营2

D 牛牛与整除分块

题意

给n,m两个数字,求n / m在n / i(i属于1~n)里面的整数从大到小排第几位

比如 n=25 ,m=9 ,(25,12,8,6,5,4,3,2,1)答案是 8

思路

因为整除分块有一个对称性,两两相乘接近n,1~sqrt(n)-1是都有的,但sqrt(n)*(sqrt(n)+1)>=n的时候整除分块是偶数个,其他时候是奇数个

比如20 sqrt(20)=4, 1 2 3 都有,按照对称性20,10,6,特判一下4*5==20,所以有两个数4和5

比如19 sqrt(19)=4, 1 2 3 都有,按照对称性20,10,6,特判一下4*5>19,所以只有一个4

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e5+10;
const int modd=1e9;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,x;
        scanf("%d%d",&n,&x);
        int k=(int)sqrt(n);
        int ge=2*k-1;
        if(k*k+k<=n){
            ge++;
        }
        if(x<k){
            printf("%d\n",x);
        }
        else{
            printf("%d\n",ge-n/x+1);
        }
    }
    return 0;
}

H 牛牛与交换排序

题意

牛牛有一个数组,数组元素是1到n的排列,即数组的值在1~n范围内,且每个数字仅出现1次。
牛牛想要将该数组变为升序排列的,他可以进行如下的操作。

首先他要确定一个长度k,k的范围在1~n之间。
接下来他将会进行若干次操作。在每轮操作中他都可以选择一段长度为k的子数组,然后进行区间的翻转操作。

他可以做任意次数的操作,但是要求他每次选择的子数组区间满足,并且区间长度等于一开始选定的k,也就是说一旦某一次操作选择了数组的某个位置进行区间翻转操作,下一次做区间翻转的位置将会比上一次更靠右。

牛牛发现,并不总是存在一个k可以使得数组排序变为有序,请你告诉牛牛是否存在一个k能够在满足规则的情况下完成排序。

思路

数组从头开始,第一个下标和值不相等,值当前的下标-下标=k,然后如果不行则no,直接用reverse(比赛的时候死磕j,看到这题时候去吃饭了……)

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e5+10;
const int modd=1e9;
int a[N];
int b[N];
int main(){
    IOS;
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];b[a[i]]=i;
    }
    int flag=0;
    int len=0;
    for(int i=1;i<=n;i++){
        if(a[i]!=i){
            if(len==0){
                len=b[i]-i;
                reverse(a+i,a+i+len+1);
            }
            else{
                if(a[i+len]==i){
                    reverse(a+i,a+i+len+1);
                    continue;
                }
                else{
                    flag=1;
                    break;
                }
            }
        }
    }
    if(flag==1)
        cout<<"no"<<endl;
    else{
        cout<<"yes"<<endl;
        cout<<len+1<<endl;
    }
    return 0;
}

I 牛牛的“质因数”

题意

求和,求一个函数f[i],i范围1~n的和,f[i]这个函数,举个例子1500=22355*5,F(1500)=223555。因子从小到大排序

思路

我想了一个线性筛的方法,但因为范围可能到10^20次,所以开了int128,每次线性筛里面每个数遇到最多一次,素数不遇到,所以f[素数]=素数,那么f[非素数]=f[非素数] * 10^位数 + f[素数]

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 4e6+10;
const ll modd=1e10;
int pri[N],a[N],tot=0,len[N];
void print(__int128 x) {
	if (!x) return;
	if (x < 0) putchar('-'), x = -x;
	print(x / 10);
	putchar(x % 10 + '0');
}
ll zhi[N];
void init(){
    for(int i=2;i<N;i++){
        if(!a[i]){
            pri[tot++]=i;
            zhi[i]=(ll)i;
        }
        ll cnt=1,k=zhi[i];
        while(k){
            cnt*=10;k/=10;
        }
        for(int j=0;j<tot&&i*pri[j]<N;j++) {
            a[i*pri[j]]=1;
            zhi[i*pri[j]]=zhi[pri[j]]*cnt+zhi[i];zhi[i*pri[j]]%=modd;
            if(i%pri[j]==0){
                break;
            }
        }
    }
}
int main(){
    init();
    int n;
    scanf("%d",&n);
    ll sum=0;//cout<<(zhi[2097152])<<endl;
    for(int i=2;i<=n;i++){
        sum+=zhi[i];sum%=mod;
    }
    printf("%lld\n",sum);
    return 0;
 }

J 牛牛想要成为hacker

题意

给了一个暴力求这个数组能不能有三角形组成,出个hack的数据,要求循环min(C(3,n),n * n * lg(n))

思路

第一想法前面斐波那契数列,然后比赛直接挂机了……C(3,n)是骗人的,其实只要斐波那契倒着来后面全是1即可

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e5+10;
const int modd=1e9;
int f[N];
int main(){
    f[1]=2;f[2]=3;
    int n;
    for(int i=3;i<=40;i++){
        f[i]=f[i-1]+f[i-2];
    }
    for(int i=41;i<N;i++){
        f[i]=1;
    }
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        printf(i==n?"%d\n":"%d ",f[i]);
    }
    return 0;
 }
posted @ 2021-02-08 20:45  ouluy  阅读(62)  评论(0编辑  收藏  举报