寒假笔记·巧用数组的模拟

与二进制数结合

例题:P1062 数列

原题地址
题目描述

给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k=3时,这个序列是:

1,3,4,9,10,12,13,…

请你求出这个序列的第N项的值(用10进制数表示)。

例如,对于k=3,N=100,正确答案应该是981。

输入输出格式

输入格式:
2个正整数,用一个空格隔开:

k N (k、N的含义与上述的问题描述一致,且3≤k≤15,10≤N≤1000)。

输出格式:
1个正整数。(整数前不要有空格和其他符号)。

输入输出样例

输入样例#1:
3 100
输出样例#1:
981
代码:
k=3时,数列为:1,3,4,9,10,12,13…
转换成三进制就是:1,10,11,100,101,110,111…
看起来像是二进制,转化成十进制看看

1,2,3,4,5,6,7…1
显然,第n项就是n.

程序就把这个过程逆回去,先把n转换成二进制,再把它当成K进制,重新转换为十进制.
AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
int qq[1010];
long long int k,n,ans=0;
int main()
{
    long long int i,t=1,j,q=1;
    scanf("%lld%lld",&k,&n);
    memset(qq,0,sizeof(qq));
    for(i=1;i<=n;i++)
    {
        qq[1]++;
        for(j=1;j<=t;j++)//处理二进制数组。
        {
            if(qq[j]>=2)
            {
                qq[j+1]++;
                qq[j]%=2;
            }
        }
        if(qq[t+1]) t++;
    }
    for(i=1;i<=t;i++)
    {
        if(qq[i]) ans=ans+q;
        q*=k;
    }
    cout<<ans<<endl;
    return 0;
}

例题:array

原题地址

Description
有一个数列 a ,长度为 n ,编号从左至右为 1…n,为了满足某种特定的规律, ai不超过 k ,且大于 1 。

现在有两种操作。

1、代表将整个数列的值加 1 ,即 a_i = (a_i mod k)+1
2、查询区间 [l,r] 中值为 x 的个数。

Input
输入包含多组测试数据,第一行为一个正整数 T(1≤T≤5) 表示有 T 组测试数据。

对于每组则测试数据,

第一行三个数n(1≤n≤10^{5} ) , k(1≤k≤20) 和q(1≤q≤10 {5} ) ,代表数组长度,值的上限和询问的次数。

第二行包含 n 个数代表 a_i 的值。

接下来有 q 行有 q 次操作。

1 代表 操作1 。

2 l r x 代表 操作2。

Output
对于每次询问输出所求值。

Sample Input 1
1
5 3 4
1 3 2 1 3
2 1 5 1
1
2 1 5 1
2 1 5 3
Sample Output 1
2
2
1
Hint
第一次操作后数组为 (1, 3, 2, 1, 3)数组有 2 个 1 。

第二次操作后数组为 (2, 1, 3, 2, 1) 。

第三次操作后数组为 (2, 1, 3, 2, 1)数组有 2 个 1 。

第四次操作后数组为 (2, 1, 3, 2, 1) 数组有 1 个 3 。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
	int T;
	int n,k,q,i,j,flag,l,r,x,ks;
	int ss[100010],qq[100010][25];
	scanf("%d",&T);
	while(T--)
	{
		ks=0;
		memset(qq,0,sizeof(qq));
	    scanf("%d%d%d",&n,&k,&q);
	    for(i=1;i<=n;i++)
		{
			scanf("%d",&ss[i]);
			for(j=1;j<=k;j++)
				qq[i][j]=qq[i-1][j];
			qq[i][ss[i]]++;
		}
		for(i=0;i<q;i++)
		{
			scanf("%d",&flag);
			if(flag==1) ks++;
			if(flag==2)
			{
				scanf("%d%d%d",&l,&r,&x);
				x=(x-ks+k*ks-1)%k+1;
				printf("%d\n",qq[r][x]-qq[l-1][x]);
			}
		}
	}
	return 0;
}

链状数组

例题:P1160 队列安排

原题地址
题目描述

一个学校里老师要将班上N个同学排成一列,同学被编号为1∼N,他采取如下的方法:

先将1号同学安排进队列,这时队列中只有他一个人;

2-N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;

从队列中去掉M(M<N))个同学,其他同学位置顺序不变。

在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。

输入输出格式

输入格式:
第1行为一个正整数N,表示了有N个同学。

第2−N行,第ii行包含两个整数k,p,其中k为小于ii的正整数,p为0或者1。若p为0,则表示将i号同学插入到k号同学的左边,p为1则表示插入到右边。

第N+1行为一个正整数M,表示去掉的同学数目。

接下来M行,每行一个正整数x,表示将xx号同学从队列中移去,如果x号同学已经不在队列中则忽略这一条指令。

输出格式:
1行,包含最多N个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。

输入输出样例

输入样例#1:
4
1 0
2 1
1 0
2
3
3
输出样例#1:
2 4 1

说明

样例解释

将同学2插入至同学1左边,此时队列为:

2 1

将同学3插入至同学2右边,此时队列为:

2 3 1

将同学4插入至同学1左边,此时队列为:

2 3 4 1

将同学3从队列中移出,此时队列为:

2 4 1

同学3已经不在队列中,忽略最后一条指令

最终队列:

2 4 1

数据范围

对于20%的数据,有N≤10;

对于40%的数据,有N≤1000;

对于100%的数据,有N, M≤100000。
代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
using namespace std;
int n,k,p,m,t,a;
int le[100010],ri[100010],qq[100010];
int main()
{
    int i,j;
    scanf("%d",&n);
    memset(qq,0,sizeof(qq));
    memset(le,0,sizeof(le));
    memset(ri,0,sizeof(ri));
    qq[1]=1;
    ri[0]=1;
    for(i=2;i<=n;i++)
    {
        scanf("%d%d",&k,&p);
        if(p==0)//入列操作
        {
            ri[le[k]]=i;
            le[i]=le[k];
            ri[i]=k;
            le[k]=i;
        }
        if(p==1)//出列操作
        {
            le[ri[k]]=i;
            le[i]=k;
            ri[i]=ri[k];
            ri[k]=i;
        }
        qq[i]=1;
    }
    scanf("%d",&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d",&t);
        if(qq[t])
        {
        a=le[t];
        ri[le[t]]=ri[t];
        le[ri[t]]=a;
        qq[t]=0;
        }
    }
    j=ri[0];
    for(i=j;ri[i]!=0;)
    {
        printf("%d ",i);
        i=ri[i];
    }
    printf("%d\n",i);
    return 0;
}

高精度&快速幂

例题:P1045 麦森数

原题地址

题目描述
形如:2^{P}-1
的素数称为麦森数,这时PP一定也是个素数。但反过来不一定,即如果PP是个素数,2^{P}-1不一定也是素数。到1998年底,人们已找到了37个麦森数。最大的一个是P=3021377,它有909526位。麦森数有许多重要应用,它与完全数密切相关。

任务:从文件中输入P(1000<P<3100000),计算2^{P}-1的位数和最后500位数字(用十进制高精度数表示)

输入输出格式
输入格式:
文件中只包含一个整数P(1000<P<3100000)

输出格式:
第一行:十进制高精度数2^{P}-1的位数。

第2-11行:十进制高精度数2^{P}-1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0)

不必验证2^{P}-1与P是否为素数。

输入输出样例
输入样例#1:
1279
输出样例#1:
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int f[1001],p,res[1001],sav[1001];//乘法要开两倍长度
void result_1()
{
    memset(sav,0,sizeof(sav));
    for(register int i=1;i<=500;i+=1)
        for(register int j=1;j<=500;j+=1)
            sav[i+j-1]+=res[i]*f[j];//先计算每一位上的值(不进位)
    for(register int i=1;i<=500;i+=1)
    {
        sav[i+1]+=sav[i]/10;//单独处理进位问题,不容易出错
        sav[i]%=10;
    }
    memcpy(res,sav,sizeof(res));//cstring库里的赋值函数,把sav的值赋给res
}
void result_2()//只是在result_1的基础上进行了细微的修改
{
    memset(sav,0,sizeof(sav));
    for(register int i=1;i<=500;i+=1)
        for(register int j=1;j<=500;j+=1)
            sav[i+j-1]+=f[i]*f[j];
    for(register int i=1;i<=500;i+=1)
    {
        sav[i+1]+=sav[i]/10;
        sav[i]%=10;
    }
    memcpy(f,sav,sizeof(f));
}
int main()
{
    scanf("%d",&p);
    printf("%d\n",(int)(log10(2)*p+1));
    res[1]=1;
    f[1]=2;//高精度赋初值
    while(p!=0)//快速幂模板
    {
        if(p%2==1)result_1();
        p/=2;
        result_2();
    }
    res[1]-=1;
    for(register int i=500;i>=1;i-=1)//注意输出格式,50个换一行,第一个不用
        if(i!=500&&i%50==0)printf("\n%d",res[i]);
        else printf("%d",res[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值