与二进制数结合
例题: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;
}