H题

题意

统计1~n“k合因子数”数量。一个数的k合因子数是指这个数所有是合数的因子的数量。

思路

使用埃氏筛标记出来[1,n]所有的合数,因为埃氏筛的模板是标记质数,要特别注意1和0既不是质数又不是合数,所以记得要改一下标记。
完成了这一步以后,我们得到一个[1,n]是否是合数的bool数组chk,要鉴别一个数x是否是合数,只需要查chk[x]即可。
然后逐个检索每个合数,得到它们的因数个数,再减去其中非因数的即可。


其实我前一个小时一直在想这题,确切的说是一直在胡思乱想,我总觉得有更好的更优美的写法,可以直接一步到位。
就是模仿欧拉筛:简单说一下,埃氏筛是筛掉所有质数的倍数,欧拉筛加了一个“不重复访问”。对于这道题而言,任何一个合数都可以表示成为若干个质数的积,那么在出质数的时候就可以递推,比如2是一个质数, 不是质数,并且 多了一个因子, 多了两个因子, 多了两个因子并且拥有4的所有因子(这里可以考虑去重但是好像没必要。
可能等我有空()会再写一下吧。

我的代码

solution

#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
bool chk[N+1];
int cnt[N];
void sieve(int n) {//埃筛 
    int p = 0;
    for(int i = 0; i <= n; i++) chk[i] = 1;
    chk[0] = chk[1] = 0;
    for(int i = 2; i <= n; i++)
        if(chk[i]) {
            chk[p++] = i;
            for(int j = 2*i; j <= n; j+=i) chk[j] = 0;  //筛去所有质数的倍数
        }
}
int f(int num) {//判断合因数个数 
    int factor[1600],m=0;
    for(int i=1; i<=num/i; i++) 
        if(num%i==0) {
            factor[++m]=i;
            if(i!=num/i)factor[++m]=num/i;
        }
    int ans=m;
    for(int i=1; i<=m; i++) 
        if(chk[factor[i]]) ans--;
    return ans;
}
int main() {
    int n,m;
    cin>>n>>m;
    sieve(n);
    chk[0]=1,chk[1]=1;
    for(int i=4; i<=n; i++) {
        if(!chk[i]) cnt[f(i)]++;
    }
    int k;
    while(m--){
        cin>>k;
        cout<<cnt[k]<<endl;
    }
    return 0;
}

埃筛模板

#include <iostream>
using namespace std;
const int N = 10005;
int prime[N];  //第i个素数
bool is_prime[N+1];  //is_prime[i]为true时表示i是素数

int sieve(int n) {//返回n以内素数的个数
    int p = 0;
    for(int i = 0; i <= n; i++) is_prime[i] = true;
    is_prime[0] = is_prime[1] = false;
    for(int i = 2; i <= n; i++) {
        if(is_prime[i]) {
            prime[p++] = i;
            for(int j = 2*i; j <= n; j+=i) is_prime[j] = false;  //筛去所有素数的倍数
        }
    }
    return p;
}

int main() {
    int n;  //枚举n以内素数
    while(cin>>n) {
        int p = sieve(n);
        cout<<p<<endl;
        for(int i = 0; i < p; i++)
            cout<< prime[i]<<" ";
        cout<<endl;
    }
    return 0;
}

判断因数个数模板

int work(int num){
    int factor[1600],m=0;
    for(int i=1;i<=num/i;i++){
        if(num%i==0){
            factor[++m]=i;
            if(i!=num/i)factor[++m]=num/i;
        }
    }
    return m;
}

别人的代码

最短的

#include <iostream>
using namespace std;
int a[100010],b[100010];
int main(){
    int n,m,k;
    cin>>n>>m;
    for(int i=2;i<=n;i++)
        for(int j=i+i;j<=n;j+=i)
            if(a[j]==0 || a[i]!=0) a[j]++;
    for(int i=0;i<=n;i++) b[a[i]]++;
    while(m--){
        cin>>k;
        cout<<b[k]<<endl;
    }
    return 0;
}

xby大佬的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
bool vis[maxn];
ll ans[maxn],res[maxn];
int n,m;
void solve(int x) {
    int tmp=x,cnt=0;
    for(int i=1; i*i<=x; i++) {
        if( x%i==0 ) {
            if(vis[i]==1) cnt++;
            if(i*i!=x && vis[x/i]==1) cnt++;
        }
    }
    ans[cnt]++;
}
int main() {
    for(int i=2; i<2e5; i++) 
        for(int j=i+i; j<2e5; j+=i)  vis[j]=1;
    cin>>n>>m;
    for(int i=1; i<=n; i++) solve(i);
    for(int i=1; i<=m; i++) cin>>res[i];
    for(int i=1; i<=m; i++) cout<<ans[res[i]]<<endl;
}

ylm大佬的

#include <bits/stdc++.h>
using namespace std;
int main(){
    int n,m;
    cin>>n>>m;
    bool v[n+1]={0};
    for(int i=2;i<=n;i++){
        if(v[i])continue;
        for(int j=i;j<=n/i;j++)v[i*j]=1;
    }
    int nums[n+1]={0},ans[n+1]={0};
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n/i;j++)
            if(v[i])  nums[i*j]++;
    for(int i=1;i<=n;i++)
        ans[nums[i]]++;
    while(m--){
        int k;
        cin>>k;
        cout<<ans[k]<<endl;
    }
}

I题

题意

汉诺塔,输入n,统计A->B,A->C,B->A,B->C,C->A,C->B的次数,以及所有移动的总步数。

思路

编码小问题

总的移动步数肯定是图片说明,但是我用pow函数wa了好几次,非常不满。
我还想到了两种表示方案:第一种是直接六个数加起来,第二种是使用移位运算符

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
int main() {
    ll a=(1<<60)-1;
    cout<<a<<endl;
    return 0;
}
//输出结果:18446744073709551615

需要注意的是因为移位运算符和流使用的是一种符号所以不能写在cout里面。
试了一下这种表示形式可以表示到ull的上限。

我的思路

我的做法就是找规律+部分打表+递推。

我先用模拟的代码(一定超时,要到60的话一下午都模拟不完)

#include <bits/stdc++.h>
#define sscc ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
unsigned long long counter[10];
void Move(char g,char p) {
    if(g=='a'&&p=='b')counter[0]++;
    else if(g=='a'&&p=='c') counter[1]++;
    else if(g=='b'&&p=='a') counter[2]++;
    else if(g=='b'&&p=='c') counter[3]++;
    else if(g=='c'&&p=='a') counter[4]++;
    else if(g=='c'&&p=='b') counter[5]++;
}
void h(int n,char a,char b,char c) {
    if(n==1)
        Move(a,c);
    else {
        h(n-1,a,c,b);
        Move(a,c);
        h(n-1,b,a,c);
    }
}
int main() {
//    int m;
//    while(~scanf("%d",&m)){
    for(int i=1; i<=20; i++) {
        memset(counter,0,sizeof(counter));
        h(i,'a','b','c');
        printf("%llu,",counter[0]);
        printf("%llu,",counter[1]);
        printf("%llu,",counter[2]);
        printf("%llu,",counter[3]);
        printf("%llu,",counter[4]);
        printf("%llu\n",counter[5]);
    }
    return 0;
}

可以输出n在[1,20]里对应的答案。
图片说明
观察可以发现每个从偶数n-1变成奇数n的过程,SUM+2^(n-1),且AB BC CA三项不变,AC BA CB有变化,且BA一直等于CB,且二者变化的差值为1。从奇数到偶数也是类似的。
所以我把它们的差值用excel算了一下,每一组分成大值和小值,发现它们是“循环翻倍”的关系,那么到了这一步,我们只需要知道前几组数据,就可以推出来后面的所有数据的之间的变化差值,进而得到所有的数据。(就不用模拟了)。

大佬的补充

A->B=(上一次)A->C->B
B->C=(上一次)B->A->C
C->A=(上一次)C->B->A
另外居然有大佬用dp做,菜鸡大概暂时理解不了https://ac.nowcoder.com/acm/contest/view-submission?submissionId=42887735

我的代码

solution

#include <bits/stdc++.h>
#define sscc ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
const int N = 1e5+7;
unsigned long long ad[61][2]= {0};
unsigned long long ans[61][6]= {0,0,0,0,0,0, 0,1,0,0,0,0, 1,1,0,1,0,0, 1,3,1,1,0,1, 4,3,1,4,2,1};
int main() {
    ad[3][0]=2;
    ad[3][1]=1;
    ad[4][0]=3;
    ad[4][1]=2;
    for(int i=5; i<=60; i++) {
        if(i&1) {
            ad[i][0]=ad[i-1][0]*2;
            ad[i][1]=ad[i][0]-1;
        } else {
            ad[i][1]=ad[i-1][1]*2;
            ad[i][0]=ad[i][1]+1;
        }
    }
    for(int i=5; i<=60; i++) {
        if(i&1) {
            ans[i][0]=ans[i-1][0];
            ans[i][1]=ans[i-1][1]+ad[i][0];
            ans[i][2]=ans[i-1][2]+ad[i][1];
            ans[i][3]=ans[i-1][3];
            ans[i][4]=ans[i-1][4];
            ans[i][5]=ans[i-1][5]+ad[i][1];
        } else {
            ans[i][0]=ans[i-1][0]+ad[i][0];
            ans[i][1]=ans[i-1][1];
            ans[i][2]=ans[i-1][2];
            ans[i][3]=ans[i-1][3]+ad[i][0];
            ans[i][4]=ans[i-1][4]+ad[i][1];
            ans[i][5]=ans[i-1][5];
        }
    }
    int n;
    while(cin>>n) {
        printf("A->B:%llu\n",ans[n][0]);
        printf("A->C:%llu\n",ans[n][1]);
        printf("B->A:%llu\n",ans[n][2]);
        printf("B->C:%llu\n",ans[n][3]);
        printf("C->A:%llu\n",ans[n][4]);
        printf("C->B:%llu\n",ans[n][5]);
        cout<<"SUM:"<<ans[n][0]+ans[n][1]+ans[n][2]+ans[n][3]+ans[n][4]+ans[n][5]<<endl;
    }
    return 0;
}

顺便一提,我中间又越界了,所以数组一定要开大一点啊
你可以看到这个循环体里只有对数据的调用,数据的处理过程其实是已经打好了表的,然后我让他们打表他们一个都没打杨文冠打了,点名表扬。

打表

#include <bits/stdc++.h>
#define sscc ios::sync_with_stdio(false)
using namespace std;
unsigned long long ans[61][6]= {0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,3,1,1,0,1,4,3,1,4,2,1,4,9,6,4,2,6,15,9,6,15,12,6,15,31,27,15,12,27,58,31,27,58,54,27,58,117,112,58,54,112,229,117,112,229,224,112,229,459,453,229,224,453,912,459,453,912,906,453,912,1825,1818,912,906,1818,3643,1825,1818,3643,3636,1818,3643,7287,7279,3643,3636,7279,14566,7287,7279,14566,14558,7279,14566,29133,29124,14566,14558,29124,58257,29133,29124,58257,58248,29124,58257,116515,116505,58257,58248,116505,233020,116515,116505,233020,233010,116505,233020,466041,466030,233020,233010,466030,932071,466041,466030,932071,932060,466030,932071,1864143,1864131,932071,932060,1864131,3728274,1864143,1864131,3728274,3728262,1864131,3728274,7456549,7456536,3728274,3728262,7456536,14913085,7456549,7456536,14913085,14913072,7456536,14913085,29826171,29826157,14913085,14913072,29826157,59652328,29826171,29826157,59652328,59652314,29826157,59652328,119304657,119304642,59652328,59652314,119304642,238609299,119304657,119304642,238609299,238609284,119304642,238609299,477218599,477218583,238609299,238609284,477218583,954437182,477218599,477218583,954437182,954437166,477218583,954437182,1908874365,1908874348,954437182,954437166,1908874348,3817748713,1908874365,1908874348,3817748713,3817748696,1908874348,3817748713,7635497427,7635497409,3817748713,3817748696,7635497409,15270994836,7635497427,7635497409,15270994836,15270994818,7635497409,15270994836,30541989673,30541989654,15270994836,15270994818,30541989654,61083979327,30541989673,30541989654,61083979327,61083979308,30541989654,61083979327,122167958655,122167958635,61083979327,61083979308,122167958635,244335917290,122167958655,122167958635,244335917290,244335917270,122167958635,244335917290,488671834581,488671834560,244335917290,244335917270,488671834560,977343669141,488671834581,488671834560,977343669141,977343669120,488671834560,977343669141,1954687338283,1954687338261,977343669141,977343669120,1954687338261,3909374676544,1954687338283,1954687338261,3909374676544,3909374676522,1954687338261,3909374676544,7818749353089,7818749353066,3909374676544,3909374676522,7818749353066,15637498706155,7818749353089,7818749353066,15637498706155,15637498706132,7818749353066,15637498706155,31274997412311,31274997412287,15637498706155,15637498706132,31274997412287,62549994824598,31274997412311,31274997412287,62549994824598,62549994824574,31274997412287,62549994824598,125099989649197,125099989649172,62549994824598,62549994824574,125099989649172,250199979298369,125099989649197,125099989649172,250199979298369,250199979298344,125099989649172,250199979298369,500399958596739,500399958596713,250199979298369,250199979298344,500399958596713,1000799917193452,500399958596739,500399958596713,1000799917193452,1000799917193426,500399958596713,1000799917193452,2001599834386905,2001599834386878,1000799917193452,1000799917193426,2001599834386878,4003199668773783,2001599834386905,2001599834386878,4003199668773783,4003199668773756,2001599834386878,4003199668773783,8006399337547567,8006399337547539,4003199668773783,4003199668773756,8006399337547539,16012798675095106,8006399337547567,8006399337547539,16012798675095106,16012798675095078,8006399337547539,16012798675095106,32025597350190213,32025597350190184,16012798675095106,16012798675095078,32025597350190184,64051194700380397,32025597350190213,32025597350190184,64051194700380397,64051194700380368,32025597350190184,64051194700380397,128102389400760795,128102389400760765,64051194700380397,64051194700380368,128102389400760765,256204778801521560,128102389400760795,128102389400760765,256204778801521560,256204778801521530,128102389400760765};
int main() {
    int n;
    while(cin>>n) {
        printf("A->B:%llu\n",ans[n][0]);
        printf("A->C:%llu\n",ans[n][1]);
        printf("B->A:%llu\n",ans[n][2]);
        printf("B->C:%llu\n",ans[n][3]);
        printf("C->A:%llu\n",ans[n][4]);
        printf("C->B:%llu\n",ans[n][5]);
        unsigned long long a=(1<<n)-1;
        cout<<"SUM:"<<ans[n][0]+ans[n][1]+ans[n][2]+ans[n][3]+ans[n][4]+ans[n][5]<<endl;
    }
    return 0;
}

写到这里发现好像移位运算符用不了
图片说明
图片说明
编译器似乎也报错移位长度大于类型位数。但是llu不是64位存储吗,拿这样的话就没办法使用移位来表示了???


我看了青竹大佬的代码明白了

ll sum = (1LL<<i) - 1;

这样就没有问题了。
(小声吐槽一下xby大佬和青竹大佬的代码一模一样。

别人的代码

最短的递推

#include <iostream>
using namespace std;
typedef unsigned long long ll;
ll q[3][3]= {0};
ll m=0,n=0,p=0,c=1,x;
int main() {
    cin>>x;
    for (ll i=2; i<=x; i++) {
        if (i&1) {
            n=m+p;
            c=p+p+1;
        }else{
            p=n+c;
            m=n+n;
        }  
    }
    cout<<"A->B:"<<p<<'\n';
    cout<<"A->C:"<<c<<'\n';
    cout<<"B->A:"<<n<<'\n';
    cout<<"B->C:"<<p<<'\n';
    cout<<"C->A:"<<m<<'\n';
    cout<<"C->B:"<<n<<'\n';
    cout<<"SUM:"<<(1LL<<x)-1<<endl;
}

记忆化搜索标程

(出题人的代码 本菜鸡看不懂)

#include<bits/stdc++.h>
using namespace std;
struct node {
    long long data[6];
    node() {
        memset(data,0,sizeof(data));
    }
    //A->B      0
    //A->C      1
    //B->A      2
    //B->C      3
    //C->A      4
    //C->B      5
};
node operator + (const node &A,const node &B) {
    node C;
    for(int i=0; i<6; ++i) {
        C.data[i]=A.data[i]+B.data[i];
    }
    return C;
}
void moveto(int x,int y,node &temp) {
    if(x==0&&y==1)++temp.data[0];
    if(x==0&&y==2)++temp.data[1];
    if(x==1&&y==0)++temp.data[2];
    if(x==1&&y==2)++temp.data[3];
    if(x==2&&y==0)++temp.data[4];
    if(x==2&&y==1)++temp.data[5];
    return;
}
node dp[3][3][3][105];
bool vis[3][3][3][105];
node hanoi(int a,int b,int c,int n) {
    if (vis[a][b][c][n])return dp[a][b][c][n];
    if (n==1) {
        moveto(a,c,dp[a][b][c][n]);
        vis[a][b][c][n]=true;
        return dp[a][b][c][n];
    }
    node temp;
    temp=temp+hanoi(a,c,b,n-1);
    moveto(a,c,temp);
    temp=temp+hanoi(b,a,c,n-1);
    vis[a][b][c][n]=true;
    return dp[a][b][c][n]=temp;
}
int n;
int main() {
    scanf("%d",&n);
    node ans=hanoi(0,1,2,n);
    printf("A->B:%lld\n",ans.data[0]);
    printf("A->C:%lld\n",ans.data[1]);
    printf("B->A:%lld\n",ans.data[2]);
    printf("B->C:%lld\n",ans.data[3]);
    printf("C->A:%lld\n",ans.data[4]);
    printf("C->B:%lld\n",ans.data[5]);
    printf("SUM:%lld\n",(1LL<<n)-1);
}

A题

题意

RBD三种格子,R右D下B都可以,看似走迷宫实际上并不是BFS,他要求统计种数,而且没有严格意义上的死路。思路就是DP,就不写了。

代码

#include<bits/stdc++.h>
using namespace std;
long long a[55][55],mod=1e9+7;
char s[55];
int main(){
    int n,m,i,j;
    cin>>n>>m;
    a[1][1]=1;
    for(i=1;i<=n;i++){
        cin>>s+1;
        for(j=1;j<=m;j++){
            if(s[j]=='D'||s[j]=='B') 
                a[i+1][j]+=a[i][j],a[i+1][j]%=mod;
            if(s[j]=='R'||s[j]=='B') 
                a[i][j+1]+=a[i][j],a[i][j+1]%=mod;
        }
    }
    cout<<a[n][m];
    return 0;
}

CD弱智签到题居然一开始没写……

D题

#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
int a[100005];
map<int,int>f;
int main(){
    int n,size=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        f[a[i]]=i;
        size=max(size,a[i]);
    }
    printf("The size of the tree is %d\n",size);
    printf("Node %d is the root node of the tree\n",a[1]);
    for(int i=1;i<=size;i++){
        printf("The father of node %d is %d,",i,(f[i]/2>=1?a[f[i]/2]:-1));
        printf(" the left child is %d,",(2*f[i]<=n?a[2*f[i]]:-1));
        printf(" and the right child is %d\n",(2*f[i]+1<=n?a[2*f[i]+1]:-1));
    }
}
#include<bits/stdc++.h>
using namespace std;
int a[1000005];
int pos[1000005];
int n;
int main(){
    memset(a,-1,sizeof(a));
    cin>>n;
    int ans=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        ans+=(a[i]!=-1);
        pos[a[i]]=i;
    }
    printf("The size of the tree is %d\n",ans);
    printf("Node %d is the root node of the tree\n",a[1]);
    for(int i=1;i<=ans;i++){
        printf("The father of node %d is %d, the left child is %d, and the right child is %d\n",i,a[pos[i]/2],a[pos[i]*2],a[pos[i]*2+1]);
    }

}

F题

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int main() {
    long long n,ans=0,cnt=0,num=0;
    string s;
    cin>>n>>s;
    for(int i=0;i<n;i++){
        if(s[i]=='1') num++,ans+=cnt;
        cnt+=num;
    }
    cout<<ans%mod<<endl;
}