数据结构专题系列 基础篇-2 数论(上)
--------------------------------------------------------------------------------
题目传送门
洛谷数论 入门组
洛谷数论 普及-组
--------------------------------------------------------------------------------
更多详见>>
--------------------------------------------------------------------------------
序
这里是数据结构专题系列 基础篇-2 数论(上)
数论是一个大家庭,涵盖了几乎所有数学和算法相关的问题,这其中包括了数列、众数、平均数、中位数、Fibonacci、进制转换、康托展开、逆康托展开、方程式以及数论与其他算法的结合体等等,让我们一起走进数论的世界,一探其中的奥秘。
本章主要涵盖了数论的基础题系列,题目可由上面的传送门获得,想要敲开数论的大门,必须先打好基础。正所谓九层之台,始于垒土;千里之行,始于足下!
本章一共36+75题,推荐先花若干时间学明白数学在算法中的思想,然后分4-8周完成习题,这样有助于巩固所学知识!
习题数量较多,难度不会过大,但这对数论、数学知识打基础至关重要!
更多内容详见 OI WIKI
接下来就是题解部分了,每道算法题都标注有对应的算法标签,对于那些易错、较难或是测试点比较特殊的题目会着重标注,本章推荐的题目有:
P2026 求一次函数解析式 | 计算几何 |
---|---|
P2368 EXCEEDED WARNING B | 数论 平方数 |
P2067 Cytus-Holyknight | 计算几何 |
P1497 木牛流马 | 排列组合 |
P1595 信封问题 | 数论 DP |
P1010 [NOIP1998 普及组] 幂次方 | 数论 DFS |
P1211 [USACO1.3]牛式 Prime Cryptarithm | 数论 枚举 |
P1258 小车问题 | 数论 方程 |
P6746 『MdOI R3』Operations | 数论 |
P5657 [CSP-S2019] 格雷码 | 数论 DFS |
P1932 A+B A-B A*B A/B A%B Problem | 数论 高精 |
P1327 数列排序 | 数论 排序 |
P4122 [USACO17DEC]Blocked Billboard B | 数论 模拟 |
P1326 足球 | 数论 |
P1348 Couple number | 数论 枚举 |
P1889 士兵站队 | 数论 中位数 |
P2719 搞笑世界杯 | 数论 DP |
--------------------------------------------------------------------------------
模板003 高精度算法模板-加/减/乘/除/取余
这里给出高精度算法的模板
模板不要求死记硬背,但要求根据算法的原理进行掌握,任何算法明确总体的思路一定是第一位的!
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e4+5; //数组大小
int a[maxn]; //输入数字1(>0)
int b[maxn]; //输入数字2(>0)
int pplus[maxn]; //加法结果
int mminus[maxn]; //减法结果
int muti[maxn]; //乘法结果
int Ta[maxn]; //被除数、除法取余结果
int Tb[maxn]; //除数
int divide[maxn]; //除法结果
string s1,s2; //输入数1、数2
void init(){ //初始化函数
cin >> s1 >> s2;
int len1 = s1.size();
int len2 = s2.size();
for(int i=0;i<len1;i++){
a[len1-i-1] = s1[i] - '0';
}
for(int i=0;i<len2;i++){
b[len2-i-1] = s2[i] - '0';
}
}
void add(int a[],int b[],int pplus[]){
int jw = 0;
int len1 = s1.size();
int len2 = s2.size();
for(int i=0;i<max(len1,len2);i++){
pplus[i] = a[i] + b[i] + jw;
jw = pplus[i] / 10;
pplus[i] %= 10;
}
if(jw){
pplus[max(len1,len2)] = jw;
for(int i=max(len1,len2);i>=0;i--){
printf("%d",pplus[i]);
}
printf("\n");
}else{
for(int i=max(len1,len2)-1;i>=0;i--){
printf("%d",pplus[i]);
}
printf("\n");
}
}
void minu(int a[],int b[],int mminus[]){
bool f_minus = true;
int len1 = s1.size();
int len2 = s2.size();
if(len2>len1){
f_minus = false;
}else if(len2 == len1){
for(int i=len2-1;i>=0;i--){
if(a[i]<b[i]){
f_minus = false;
break;
}else if(a[i]>b[i]){
break;
}
}
}
if(f_minus){
for(int i=0;i<=max(len1,len2)-1;i++){
mminus[i] = mminus[i] + a[i] - b[i];
if(mminus[i]<0){
mminus[i] += 10;
mminus[i+1]--;
}
}
}else{
for(int i=0;i<=max(len1,len2)-1;i++){
mminus[i] = mminus[i] + b[i] - a[i];
if(mminus[i]<0){
mminus[i] += 10;
mminus[i+1]--;
}
}
}
int cnt = max(len1,len2);
for(int i=cnt-1;i>=0;i--){
if(mminus[i] == 0){
cnt--;
}else{
break;
}
}
if(!cnt){
printf("0");
}else{
if(!f_minus){
printf("-");
}
for(int i=cnt-1;i>=0;i--){
printf("%d",mminus[i]);
}
}
printf("\n");
}
void mutiply(int a[],int b[],int muti[]){
int len1 = s1.size();
int len2 = s2.size();
for(int i=0;i<len1;i++){
for(int j=0;j<len2;j++){
muti[i+j] += a[i]*b[j];
}
}
int jw = 0;
for(int i=0;i<len1+len2-1;i++){
muti[i] = muti[i] + jw;
jw = muti[i]/10;
muti[i] %= 10;
}
int cnt = 0;
while(jw){
muti[len1+len2+cnt-1] = jw%10;
jw/=10;
cnt++;
}
for(int i=len1+len2+cnt-2;i>=0;i--){
printf("%d",muti[i]);
}
printf("\n");
}
int compare(int a[], int b[]) {
if(a[0]>b[0]){
return 1;
}else if(a[0]<b[0]){
return -1;
}
for (int i=a[0];i>0;i--){
if (a[i]>b[i]){
return 1;
}else if(a[i]<b[i]){
return -1;
}
}
return 0;
}
void shift(int a[],int b[],int dis){
for (int i=1;i<=a[0];i++){
b[i+dis-1]=a[i];
}
b[0]=a[0]+dis-1;
}
void dividen(int a[],int b[],int divide[]){
int len1 = s1.size();
int len2 = s2.size();
Ta[0] = len1;
for(int i=0;i<len1;i++){
Ta[len1-i] = s1[i]-'0';
}
Tb[0] = len2;
for(int i=0;i<len2;i++){
Tb[len2-i] = s2[i]-'0';
}
if(0==compare(Ta,Tb)){
printf("1\n0\n");
}else if (-1==compare(Ta,Tb)) {
printf("0\n");
cout << s1 << endl;
}else {
int tmp[maxn];
divide[0] = Ta[0]-Tb[0]+1;
for(int i=divide[0];i>0;i--) {
memset(tmp,0,sizeof(tmp));
shift(Tb,tmp,i);
while(compare(Ta,tmp)>=0) {
divide[i]++;
for(int j=1;j<=Ta[0];j++){
if(Ta[j]<tmp[j]) {
Ta[j+1]--;
Ta[j] += 10;
}
Ta[j] -= tmp[j];
}
int k = Ta[0];
while(Ta[k]==0) {
k--;
}
Ta[0]=k;
}
}
while (divide[0]>0 && divide[divide[0]]==0) {
divide[0]--;
}
for (int i=divide[0]; i>0; i--) {
printf("%d",divide[i]);
}
printf("\n");
if(0==Ta[0]) {
printf("0\n");
}else{
for(int i=Ta[0];i>0;i--){
printf("%d",Ta[i]);
}
printf("\n");
}
}
}
int main(){
init();
add(a,b,pplus);
minu(a,b,mminus);
mutiply(a,b,muti);
dividen(a,b,divide);
return 0;
}
模板006 分数类
这里给出分数类的模板(带约分函数)
模板不要求死记硬背,但要求根据算法的原理进行掌握,任何算法明确总体的思路一定是第一位的!
#include<bits/stdc++.h>
using namespace std;
struct Fraction{ //分数类
int up; //分子
int down; //分母
};
int gcd(int a,int b){ //求gcd最大公约数
if(b==0){
return a;
}else{
return gcd(b,a%b);
}
}
Fraction reduction(Fraction f){ //约分
if(f.up == 0){
f.down = 1;
}
if(f.down < 0 ){
f.up = -f.up;
f.down = -f.down;
}
int g = gcd(max(f.up,f.down),min(f.up,f.down));
f.up /= g;
f.down /=g;
return f;
}
int main(){
return 0;
}
模板007 全排列
这里给出全排列的模板
模板不要求死记硬背,但要求根据算法的原理进行掌握,任何算法明确总体的思路一定是第一位的!
#include<bits/stdc++.h>
using namespace std;
int N;
int a[15];
bool hashT[15];
void dfs(int depth){
if(depth == N){
for(int i=0;i<N;i++){
cout << a[i];
}
printf("\n");
}else{
for(int i=1;i<=N;i++){
if(!hashT[i]){
hashT[i] = true;
a[depth] = i;
dfs(depth+1);
hashT[i] = false;
}
}
}
}
int main(){
cin >> N;
dfs(0);
return 0;
}
模板008 全组合
这里给出全组合的模板
模板不要求死记硬背,但要求根据算法的原理进行掌握,任何算法明确总体的思路一定是第一位的!
#include<bits/stdc++.h>
using namespace std;
int N,R;
int a[25];
bool hashT[25];
void dfs(int depth,int index){
if(depth == R){
for(int i=0;i<R;i++){
cout << a[i];
}
printf("\n");
}else{
for(int i=index;i<=N;i++){
if(!hashT[i]){
hashT[i] = true;
a[depth] = i;
dfs(depth+1,i+1);
hashT[i] = false;
}
}
}
}
int main(){
scanf("%d%d",&N,&R);
dfs(0,1);
return 0;
}
--------------------------------------------------------------------------------
难度: 入门
P2524 Uim的情人节礼物·其之弐
算法标签: 数论 + 康托展开
注意点: 康托展开模板题,公式如下:
X=a[n] * (n-1)! +a[n-1]*(n-2)!+…+a[1] * 0!。
#include<bits/stdc++.h>
using namespace std;
char a[10];
int main(){
int N;
cin >> N;
for(int i=0;i<N;i++){
cin >> a[i];
}
int sum = 0;
while(prev_permutation(a,a+N)){
sum++;
}
sum++;
cout << sum << endl;
return 0;
}
P1482 Cantor表(升级版)
算法标签: 数论 + 模拟
注意点: 约分gcd
#include<bits/stdc++.h>
using namespace std;
int gcd (int a,int b){
if(a<b){
int tmp=a;
a=b;
b=tmp;
}
while(a%b){
int tmp = a%b;
a=b;
b=tmp;
}
return b;
}
int main(){
int a1,a2,b1,b2;
scanf("%d/%d",&a1,&b1);
scanf("%d/%d",&a2,&b2);
int a =(a1)*(a2);
int b =(b1)*(b2);
int tmp = gcd(a,b);
a=a/tmp;
b=b/tmp;
cout << b << " " << a <<endl;
return 0;
}
P1146 硬币翻转
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int a[101];
int main(){
int N;
cin >> N;
cout << N << endl;
int j = 1;
for(int i=0;i<N;i++){
int tmp = N-1;
int p=(j+i)%N;
while(tmp){
a[p] = a[p] ^ 1;
p = (p+1)%N;
tmp--;
}
for(int k=0;k<N;k++){
cout << a[k];
}
cout << endl;
}
return 0;
}
P1615 西游记公司
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
string x,y;
cin >> x >> y;
int k;
cin >> k;
int a[3]={0},b[3]={0};
int p=0;
for(int i=0;i<x.length();){
if(x[i]!=':'){
a[p] = a[p]*10 +(x[i]-'0');
i++;
}else{
i++;
p++;
}
}
p=0;
for(int i=0;i<y.length();){
if(y[i]!=':'){
b[p] = b[p]*10 +(y[i]-'0');
i++;
}else{
i++;
p++;
}
}
for(int i=2;i>=0;i--){
if(b[i]<a[i]){
b[i-1]--;
b[i]+=60;
}
b[i]-=a[i];
}
long long result = b[0]*3600 +b[1]*60 +b[2];
result = result*k;
cout << result <<endl;
return 0;
}
P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here
算法标签: 数论 + 字符串
#include<bits/stdc++.h>
using namespace std;
int main(){
char str1[8],str2[8];
cin >> str1 >> str2;
int mutiply=1;
for(int i=0;i<strlen(str1);i++){
mutiply = mutiply *(str1[i]-'A'+1);
}
int num1 = mutiply % 47;
mutiply = 1;
for(int i=0;i<strlen(str2);i++){
mutiply = mutiply *(str2[i]-'A'+1);
}
int num2 =mutiply % 47;
if(num1==num2){
cout << "GO"<<endl;
}else{
cout <<"STAY"<<endl;
}
return 0;
}
P1554 梦中的统计
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
int M,N;
int a[11];
memset(a,0,sizeof(a));
cin >> M >> N;
for(int i=M;i<=N;i++){
int k=i;
while(k>0){
a[k%10]++;
k/=10;
}
}
for(int i=0;i<10;i++){
cout << a[i] << " ";
}
cout << endl;
return 0;
}
P2043 质因子分解
算法标签: 数论 + 素数
#include<bits/stdc++.h>
using namespace std;
int a[10001]={1,1,0};
int c[1500];
int p[1500];
int main(){
int N;
cin >>N;
memset(p,0,sizeof(p));
for(int i=2;i<=10000;i++){
for(int j=2*i;j<=10000;j=j+i){
if(!a[j]){
a[j]=1;
}
}
}
int j=0;
for(int i=0;i<=10000;i++){
if(a[i]==0){
c[j]=i;
j++;
}
}
for(int k=2;k<=N;k++){
int tmp=k;
for(int i=0;i<j && tmp>1;){
if(tmp%c[i]==0){
tmp/=c[i];
p[i]++;
}else{
i++;
}
}
}
for(int i=0;i<j;i++){
if(p[i]){
cout << c[i] <<" "<<p[i]<<endl;
}
}
return 0;
}
P2669 [NOIP2015 普及组] 金币
算法标签: 数论 + 高斯公式1+2+…+n
#include<bits/stdc++.h>
using namespace std;
int main(){
int sum =0;
int K;
cin >> K;
int n=1;
while(n*(n+1)/2<K){
n++ ;
}
n--;
for(int i=1;i<=n;i++){
sum = sum + i * i;
}
K -=(n*(n+1))/2;
while(K>0){
sum += (n+1);
K--;
}
cout << sum <<endl;
return 0;
}
P5534 【XR-3】等差数列
算法标签: 数论 + 等差数列公式
#include<bits/stdc++.h>
using namespace std;
int main(){
long long sum;
long long a1,a2,n;
cin >> a1>>a2>>n;
long long d=a2-a1;
sum = a1*n +(n-1)*n/2*d;
cout << sum << endl;
return 0;
}
P1720 月落乌啼算钱(斐波那契数列)
算法标签: 数论 + Fibonacci
#include<bits/stdc++.h>
using namespace std;
int main(){
double a[50]={0,1,1};
int N;
cin >> N;
for(int i=3;i<=48;i++){
a[i] = a[i-1]+a[i-2];
}
printf("%.2lf",a[N]);
return 0;
}
P1035 [NOIP2002 普及组] 级数求和
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
int k;
cin >> k;
int n=1;
double sum=0.0;
do{
sum=sum+1.0/n;
n++;
}while(k>=sum);
cout << n-1;
return 0;
}
P1304 哥德巴赫猜想
算法标签: 数论 + 素数
#include<bits/stdc++.h>
using namespace std;
bool t[10001]={1,1,0};
int main(){
int N;
cin >>N;
for(int i=2;i<=N;i++){
for(int j=2*i;j<=N;j+=i){
if(t[i]){
continue;
}else{
t[j]=1;
}
}
}
for(int i=4;i<=N;i=i+2){
for(int j=2;j<N-1;j++){
if(!t[j] && !t[i-j]){
cout << i << "=" << j <<"+"<< i-j<<endl;
break;
}
}
}
}
P1150 Peter的烟
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,k;
cin >> n >> k;
int oral = n;
int count = 0;
while(n/k){
count = count + n/k;
n = n - n/k*k +n/k;
}
cout << oral+count << endl;
return 0;
}
P1876 开灯
算法标签: 数论
注意点: 公式结论题,完全平方数
#include<bits/stdc++.h>
using namespace std;
int main(){
long long N;
cin >> N;
for(long long i=1;i*i<=N;i++){
cout << i*i << " ";
}
cout <<endl;
return 0;
}
P1888 三角函数
算法标签: 数论 + 排序
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[3];
for(int i=0;i<3;i++){
cin >> a[i];
}
sort(a,a+3);
int b=a[0];
int c=a[2];
while(c%b){
int tmp =c % b;
c = b;
b = tmp;
}
a[0]/=b;
a[2]/=b;
cout << a[0] <<"/"<< a[2] <<endl;
return 0;
}
P4413 [COCI2006-2007#2] R2
算法标签: 数论 + 方程
#include<bits/stdc++.h>
using namespace std;
int main(){
int R1,S;
cin >> R1 >>S;
cout << 2*S-R1<<endl;
return 0;
}
P1590 失踪的7
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >>t;
for(int i=0;i<t;i++){
long long n;
cin >> n;
long long sum =1;
long long result =0;
for(;n;){
int tmp = n % 10;
if(tmp>=7){
tmp--;
}
result = result + sum * tmp;
sum *=9;
n=n/10;
}
cout << result << endl;
}
return 0;
}
P1075 [NOIP2012 普及组] 质因数分解
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,p1,p2;
cin >> n;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
p1=i;
p2=n/i;
break;
}
}
if(p1>=p2){
cout <<p1;
}else{
cout <<p2;
}
return 0;
}
P1851 好朋友
算法标签: 数论 + 枚举
#include<bits/stdc++.h>
using namespace std;
int a[]={220,1184,2620,5020,6232,10744,12285,17296,63020};
int b[]={284,1210,2924,5564,6368,10856,14595,18416,76084};
int main(){
int s;
cin >> s;
for(int i=0;i<9;i++){
if(a[i]>=s){
cout << a[i] << " "<<b[i]<<endl;
return 0;
}
if(b[i]>=s){
cout << b[i] << " "<< a[i]<< endl;
return 0;
}
}
return 0;
}
P1634 禽兽的传染病
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
long long x,n,result=1;
cin >> x >> n;
for(int i=0;i<n;i++){
result*=(1+x);
}
cout << result;
return 0;
}
P1652 圆
算法标签: 计算几何
#include<bits/stdc++.h>
using namespace std;
int x[51];
int y[51];
int r[51];
int dis(int x1,int y1,int x2,int y2,int r){
double distance = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
if(distance<=r){
return 1;
}else{
return 0;
}
}
int main(){
int n;
cin >> n;
for(int i=0;i<n;i++){
cin >> x[i];
}
for(int i=0;i<n;i++){
cin >> y[i];
}
for(int i=0;i<n;i++){
cin >> r[i];
}
int sum = 0;
int x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
for(int i=0;i<n;i++){
if((!dis(x1,y1,x[i],y[i],r[i])&&dis(x2,y2,x[i],y[i],r[i]))||
(dis(x1,y1,x[i],y[i],r[i])&&!dis(x2,y2,x[i],y[i],r[i]))){
sum ++;
}
}
cout << sum << endl;
return 0;
}
P1887 乘积最大3
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
long long N,M;
cin >> N >> M;
long long ave = N/M;
int tmp= N%M;
for(int i=0;i<M-tmp;i++){
cout << ave << " ";
}
for(int i=0;i<tmp;i++){
cout << ave+1 << " ";
}
cout <<endl;
return 0;
}
P2525 Uim的情人节礼物·其之壱
算法标签: 数论 + 排列
#include<bits/stdc++.h>
using namespace std;
int a[10];
int b[10];
int main(){
int N;
cin >> N;
for(int i=0;i<N;i++){
cin >> a[i];
}
if(prev_permutation(a,a+N)){
for(int i=0;i<N;i++){
cout << a[i] <<" ";
}
cout <<endl;
}else{
cout << "ERROR" << endl;
}
return 0;
}
P2415 集合求和
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
int i=0;
int a[1000];
memset(a,0,sizeof(a));
char ch[1000];
gets(ch);
int tmp=0;
int j=0;
for(int i=0;i<strlen(ch);i++){
if(ch[i]>='0' && ch[i]<='9'){
tmp = tmp*10 + (ch[i] -'0');
}else{
a[j]=tmp;
j++;
tmp=0;
}
}
if(tmp!=0){
a[j]=tmp;
j++;
}
long long sum =0;
for(int i=0;i<j;i++){
sum += a[i];
}
double coff = pow(2,j-1);
long long co =int(coff);
sum = sum * co;
cout << sum << endl;
return 0;
}
P2705 小球
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
int R,B,C,D,E;
cin >> R >> B;
cin >> C >> D >> E;
int k;
if(R>=B){
k=1;
}else{
k=0;
}
int sum = 0;
if(C+D>=2*E){
sum = R*C+B*D;
cout << sum <<endl ;
}else{
if(k==1){
sum = B*2*E +(R-B)*C;
cout << sum <<endl;
}else{
sum = R*2*E +(B-R)*D;
cout << sum <<endl;
}
}
return 0;
}
P1425 小鱼的游泳时间
算法标签: 数论 + 模拟
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
int main(){
int a=0,b=0,c=0,d=0,e=0,f=0;
scanf("%d%d%d%d",&a,&b,&c,&d);
e=c-a;
f=d-b;
if(f<0){
e--;
f+=60;
}
printf("%d %d",e,f);
return 0;
}
P7257 [COCI2009-2010#3] FILIP
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
cin >> a >> b;
int a1 = 0,b1 = 0;
while(a){
a1 = a1*10 + (a%10);
a/=10;
}
while(b){
b1 = b1*10 + (b%10);
b/=10;
}
cout << max(a1,b1);
return 0;
}
P7573 「PMOI-3」公平の意
算法标签: 数论 + 模拟
注意点: 一个蛋糕不用切,因此n=1时,要特判0的输出,其余情况输出
⌈
n
2
⌉
\lceil \frac{n}{2} \rceil
⌈2n⌉
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t--){
int n;
cin >> n;
if(n == 1){
cout << "0" << endl;
}else{
int ans = ceil(n*1.0/2);
cout << ans << endl;
}
}
return 0;
}
P6421 [COCI2008-2009#2] RESETO
算法标签: 数论 + 素数筛
注意点: 埃拉托色尼筛法模板题
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
int n,k;
int a[maxn] = {1,1,0};
int main(){
cin >> n >> k;
for(int i=2;i<=n;i++){
if(!a[i]){
k--;
if(k==0){
cout << i << endl;
return 0;
}
for(int j=2*i;j<=n;j+=i){
if(!a[j]){
a[j] = 1;
k--;
}
if(k==0){
cout << j << endl;
return 0;
}
}
}
}
return 0;
}
P7933 [COCI2007-2008#5] PASCAL
算法标签: 数论 + 素数
注意点: 特判n=1!
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
if(n==1){
cout << "0" << endl;
}else{
int u = -1;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
u = i;
break;
}
}
if(u == -1){
cout << n-1 << endl;
}else{
cout << n - n/u << endl;
}
}
return 0;
}
P7199 [COCI2019-2020#1] Trol
算法标签: 数论
注意点: 这是一道好题!一个数N各位反复相加的总和为(N-1)%9+1,每10个数字一轮循环,因此枚举最后的(不足10个)数字即可
#include<bits/stdc++.h>
using namespace std;
int main(){
int Q;
long long l,r;
cin.tie(0);
cout.tie(0);
cin >> Q;
for(int i=0;i<Q;i++){
cin >> l >> r;
long long k = (r-l)/9;
long long sum = 0;
sum += 45*k;
for(long long i=l+9*k;i<=r;i++){
sum += (i-1) % 9 + 1;
}
printf("%lld\n",sum);
}
return 0;
}
P7892 『JROI-3』R.I.P.
算法标签: 数论
注意点: 求一个数分解成两个最大的公因数
#include<bits/stdc++.h>
using namespace std;
int main(){
int T;
cin >> T;
while(T--){
int n,m;
cin >> n >> m;
int u = -1;
for(int i=sqrt(n);i>=2;i--){
if(n%i==0){
u = i;
break;
}
}
int need;
if(u == -1){
need = (2+(n+1)) * 2;
}else{
need = ((u+1) + (n/u+1))*2;
}
if(need <= m){
printf("Good\n");
}else{
printf("Miss\n");
}
}
return 0;
}
P7772 [COCI 2009-2010 #2] FAKTOR
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
int A,I;
cin >> A >> I;
cout << (I-1)*A + 1 << endl;
return 0;
}
P7441 「EZEC-7」Erinnerung
算法标签: 数论
注意点: x=0,y=0要分4种情况讨论并特判!
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
int T;
cin >> T;
while(T--){
LL x,y,k;
cin >> x >> y >> k;
LL t = max(x,y);
if(t == 0){
cout << "0" << endl;
}else if(x==0 && y){
if(k%y == 0){
cout << "1" << endl;
}else{
cout << "0" << endl;
}
}else if(x && y==0){
if(k%x == 0){
cout << "1" << endl;
}else{
cout << "0" << endl;
}
}else{
cout << k/t << endl;
}
}
return 0;
}
P7226 [COCI2015-2016#3] POT
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
int ans = 0;
cin >> n;
while(n--){
int p;
cin >> p;
int e = p%10;
p/=10;
int t = 1;
while(e--){
t *= p;
}
ans += t;
}
cout << ans << endl;
return 0;
}
P7909 [CSP-J 2021] 分糖果
算法标签: 数论 + 模拟
注意点: 取余数的最大值即可
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,L,R;
cin >> n >> L >> R;
if(R-L>=n){
cout << n-1 << endl;
}else{
int p1 = L%n;
int p2 = R%n;
if(p1<=p2){
cout << p2 << endl;
}else{
cout << n-1 << endl;
}
}
return 0;
}
--------------------------------------------------------------------------------
难度: 普及-
P1579 哥德巴赫猜想(升级版)
算法标签: 数论 + 素数
注意点: 三层循环,按序枚举,有结果即字典序最小输出
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[20000]={1,1,0};
int result[5000];
int N;
cin >> N;
for(int i=2;i<20000;i++){
if(a[i]){
continue;
}
for(int j=i*i;j<20000;j+=i){
a[j]=1;
}
}
int j=0;
for(int i=2;i<=N;i++){
if(!a[i]){
result[j]=i;
j++;
}
}
for(int i=0;i<j;i++){
for(int p=0;p<j;p++){
for(int q=0;q<j;q++)
if(result[i]+result[p]+result[q]==N){
cout << result[i] << " " << result[p] << " " << result[q] <<endl;
return 0;
}
}
}
return 0;
}
P2026 求一次函数解析式
算法标签: 计算几何
注意点: 分数约分
#include<bits/stdc++.h>
using namespace std;
struct Fraction{
int up;
int down;
};
Fraction reduction(Fraction f){
if(f.down<0){
f.up = -f.up;
f.down = -f.down;
}
if(f.up == 0){
f.down = 1;
}
int g = __gcd(abs(max(f.up,f.down)),abs(min(f.up,f.down)));
f.up /= g;
f.down /= g;
return f;
}
int main(){
int x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
Fraction k;
k.up = (y2-y1);
k.down = (x2-x1);
k = reduction(k);
Fraction b;
b.up = y1*k.down - k.up*x1;
b.down = k.down;
b = reduction(b);
cout << "y=";
if(k.down == 1){
if(k.up!=0){
printf("%dx",k.up);
}
}else{
printf("%d/%d*x",k.up,k.down);
}
if(b.down == 1){
if(b.up!=0){
printf("%+d\n",b.up);
}
}else{
printf("%+d/%d\n",b.up,b.down);
}
return 0;
}
P1755 斐波那契的拆分
算法标签: Fibonacci
注意点: 从后向前枚举Fibonacci数,存储在栈中,然后逆序输出
若采用dfs搜索,则会发生超时(TLE)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e9+5;
int f[105] = {1,1};
int cnt;
int n;
stack<int> s;
void init(){
for(int i=2;;i++){
f[i] = f[i-1] + f[i-2];
if(f[i] > maxn){
cnt = i;
break;
}
}
}
int main(){
int t;
cin >> t;
init();
while(t--){
cin >> n;
int n1 = n;
for(int i=cnt;i>=0 && n;i--){
if(f[i]>n){
continue;
}else{
n -= f[i];
s.push(f[i]);
}
}
printf("%d=",n1);
int t = 0;
while(!s.empty()){
if(t == 0){
printf("%d",s.top());
}else{
printf("+%d",s.top());
}
t++;
s.pop();
}
printf("\n");
}
return 0;
}
P2368 EXCEEDED WARNING B
算法标签: 数论 + 平方数
注意点: 一道好题!这道题考察了对于平方数的理解
当n<=8时,无解
当n=9时 有8个满足要求的数:
111111111
119357639
380642361
388888889
611111111
619357639
880642361
888888889
容易知道,某个数的平方为987654321时,以这个数结尾的数,它的平方也是987654321
所以当n>9时,满足条件的数只能以上述八个数结尾
当n=10时,由于最高位不为0,所以有8*9=72个
n=11时,720个;n=12时,7200个…以此类推
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
if(n<=8){
cout << "0";
}else{
if(n == 9){
cout << "8";
}else{
cout << "72";
for(int i=10;i<n;i++){
cout << "0";
}
}
}
return 0;
}
P2067 Cytus-Holyknight
算法标签: 计算几何
注意点: 这道题的易错点非常多:
1.可能包含以下几种解析表达式:y=kx+b,y=b,x=b,当线竖直的时候,需要特判斜率
2.题目存在着许多坐标不完整的情况,比如:
样例1:
3
010
111
x1x
样例4:
3
0x0
1x1
010
样例10:
15
000001000000000
000001000000000
000001000000000
000001000000000
000001000000000
000001000000000
000001000000000
x1111111111111x
000001000000000
000001000000000
000001000000000
000001000000000
000001000000000
000001000000000
000001000000000
需要在这种情况下识别坐标
3.注意0.0000在k or b上的输出!
#include<bits/stdc++.h>
using namespace std;
char s[20][20];
int main(){
int n;
cin >> n;
for(int i=1;i<=n;i++){
scanf("%s",s[i]+1);
}
int row,column;
bool flag1 = true;
bool flag2 = true;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s[i][j] == '1'){
bool f1 = true;
bool f2 = true;
for(int k=1;k<=n;k++){
if(s[k][j] == '1' || s[k][j] == 'x'){
continue;
}else{
f1 = false;
break;
}
}
if(f1){
column = j;
flag1 = false;
}
for(int k=1;k<=n;k++){
if(s[i][k] == '1' || s[i][k] == 'x'){
continue;
}else{
f2 = false;
break;
}
}
if(f2){
row = i;
flag2 = false;
}
}
if(!flag1 && !flag2){
break;
}
}
if(!flag1 && !flag2){
break;
}
}
int cnt = 2;
int x1,y1,x2,y2;
for(int i=1;i<=n && cnt;i++){
for(int j=1;j<=n && cnt;j++){
if(s[i][j] == 'x'){
if(cnt>1){
x1 = j-column;
y1 = row-i;
}else{
x2 = j-column;
y2 = row-i;
}
cnt--;
}
}
}
double k,b;
if(x1==x2){
printf("x=%.4lf",x1*1.0);
return 0;
}else{
k = (1.0*(y2-y1))/(1.0*(x2-x1));
b = (y1-k*x1);
}
printf("y=");
if(fabs(k)<1e-4){
if(fabs(b)<1e-4){
printf("0.0000\n");
}else{
printf("%.4lf\n",b);
}
}else{
printf("%.4lfx",k);
if(fabs(b)<1e-4){
}else{
printf("%+.4lf\n",b);
}
}
return 0;
}
P1497 木牛流马
算法标签: 排列组合
注意点: 可重复元素的排列数目需除去重复的元素自排列个数
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
int n,k,h;
cin >> n >> k >> h;
LL ans = 1;
for(int i=1;i<=k;i++){
ans *= (n-i+1)*(n-i+1);
}
for(int i=1;i<=h;i++){
int t;
scanf("%d",&t);
for(int j=1;j<=t;j++){
ans /= j;
}
}
printf("%lld\n",ans);
return 0;
}
P1143 进制转换
算法标签: 数论 + 进制
#include<bits/stdc++.h>
using namespace std;
int a[105];
int ans = 0;
int main(){
int n;
cin >> n;
char s[105];
scanf("%s",s);
int m;
cin >> m;
int id = 0;
for(int i=0;i<strlen(s);i++){
if(n>10 && s[i]>='A'){
id = id*n + s[i] - 'A' + 10;
}else{
id = id*n + s[i] - '0';
}
}
while(id){
int t = id % m;
a[ans++] = t;
id /= m;
}
for(int i=ans-1;i>=0;i--){
if(a[i]>=10){
printf("%c",a[i]-10+'A');
}else{
printf("%c",a[i]+'0');
}
}
printf("\n");
return 0;
}
P1372 又是毕业季I
算法标签: 数论 + gcd
注意点: 默契度最大的元素就是取最大公约数最小的数,
min
(
g
c
d
)
\min(gcd)
min(gcd)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,k;
cin >> n >> k;
cout << n/k <<endl;
return 0;
}
P1458 [USACO2.1]顺序的分数 Ordered Fractions
算法标签: 数论 + 排序
注意点: 注意数组的空间要大于16000,注意排序判等时误差设置尽可能小[测试点3]
#include<bits/stdc++.h>
using namespace std;
struct Fraction{
int up;
int down;
double res;
};
Fraction f[20005];
int cnt = 0;
int gcd(int a,int b){
if(b == 0){
return a;
}else{
return gcd(b,a%b);
}
}
Fraction reduction(Fraction f){
if(f.up == 0){
f.down = 1;
}
if(f.down < 0){
f.up = -f.up;
f.down = -f.down;
}
int g = gcd(f.down,f.up);
f.up /= g;
f.down /= g;
return f;
}
bool cmp(Fraction f1,Fraction f2){
return f1.res < f2.res;
}
int main(){
int n;
cin >> n;
for(int i=0;i<=n;i++){
for(int j=i;j<=n;j++){
f[cnt].up = i;
f[cnt].down = j;
f[cnt] = reduction(f[cnt]);
f[cnt].res = (1.0*f[cnt].up)/(1.0*f[cnt].down);
cnt++;
}
}
sort(f,f+cnt,cmp);
for(int i=0;i<cnt;i++){
if(fabs(f[i+1].res-f[i].res)<1e-6){
continue;
}else{
printf("%d/%d\n",f[i].up,f[i].down);
}
}
return 0;
}
P1024 [NOIP2001 提高组] 一元三次方程求解
算法标签: 方程 + 分治
注意点: 依次枚举-100 - +100之间的每个区间[x,x+1),检查方程的根是否落在这个区间内部,此外,注意特殊情形——端点处取得
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-4;
double a,b,c,d;
int count1 = 0;
double p[5];
double f(double x,double a,double b,double c,double d){
double ans = a*x*x*x + b*x*x + c*x + d;
return ans;
}
void part(double left,double right,double a,double b,double c,double d){
if(fabs(f(left,a,b,c,d))<eps){
p[count1++] = left;
return;
}
if(fabs(right-left)<eps){
p[count1++] = left;
return;
}
double mid = (left+right)/2.0;
if(f(left,a,b,c,d)*f(mid,a,b,c,d)<=0){
return part(left,mid,a,b,c,d);
}
if(f(mid,a,b,c,d)*f(right,a,b,c,d)<=0){
return part(mid,right,a,b,c,d);
}
}
int main(){
cin >> a >> b >> c >> d;
double left,right;
for(int i=-100;i<=100;i++){
left = i*1.0;
right = left + 1.0 - eps;
part(left,right,a,b,c,d);
}
for(int i=0;i<3;i++){
printf("%.2lf ",p[i]);
}
return 0;
}
P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题
算法标签: 数论 + gcd/lcm
注意点: 由于两个数的乘积最大不超过xy,因此依次枚举1-sqrt(x*y)内所有的数,逐一进行判断!
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
int a1 = max(a,b);
int b1 = min(a,b);
while(a1%b1){
int tmp=b1;
b1=a1%b1;
a1=tmp;
}
return b1;
}
int main(){
int x,y;
cin >> x >> y;
if(y==0 || x==0 ||y%x!=0){
cout << "0" <<endl;
return 0;
}else if(y/x==1){
cout << "1" <<endl;
return 0;
}
int ans=0;
for(int i=1;i<=sqrt(1ll*x*y);i++){
if(1ll*x*y%i==0 && gcd((1ll*x*y)/i,i)==x){
ans++;
}
}
ans = ans * 2;
cout << ans << endl;
return 0;
}
P1044 [NOIP2003 普及组] 栈
算法标签: 数论 + Catalan数
注意点: 卡特兰数模板题
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
long long ans = 1;
cin >>n;
for(int i=1;i<=n;i++){
ans = ans * (n+i)/i;
}
ans = ans /(n+1);
cout << ans << endl;
}
P1595 信封问题
算法标签: 数论 + 动态规划
注意点: 递推式:D(n) = (n-1) * [D(n-1) + D(n-2)]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a[25]={0,0,1,2};
int main(){
int n;
cin >> n;
for(int i=4;i<=n;i++){
a[i] = (i-1)*(a[i-1]+a[i-2]);
}
printf("%lld\n",a[n]);
return 0;
}
P1591 阶乘数码
算法标签: 数论 + 高精
注意点: 高精度*低精度,按数组逆序计算
#include<bits/stdc++.h>
using namespace std;
int a[3005];
int main(){
int t;
cin >> t;
while(t--){
int n;
int a1;
cin >> n >> a1;
memset(a,0,sizeof(a));
a[0] = 1;
int cnt = 1;
for(int i=1;i<=n;i++){
int jw = 0;
for(int j=0;j<cnt;j++){
a[j]*=i;
}
for(int j=0;j<cnt;j++){
a[j] += jw;
jw = a[j]/10;
a[j] %= 10;
}
while(jw){
a[cnt] = jw%10;
cnt++;
jw/=10;
}
}
int ans = 0;
for(int i=0;i<cnt;i++){
if(a[i] == a1){
ans++;
}
}
cout << ans << endl;
}
return 0;
}
P2241 统计方形(数据加强版)
算法标签: 数论
注意点: 找规律
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
cin >> a >> b;
long long zcount = 0;
long long ccount =0;
int minsize = min(a,b);
int maxsize = max(a,b);
for(int i=1;i<=minsize;i++){
zcount = zcount + (a-i+1)*(b-i+1);
}
cout << zcount << " ";
for(int i=1;i<=b;i++){
for(int j=i+1;j>i && j<=a;j++){
ccount = ccount +(b-i+1)*(a-j+1);
}
}
for(int j=1;j<=a;j++){
for(int i=j+1;i>j && i<=b;i++){
ccount = ccount + (b-i+1)*(a-j+1);
}
}
cout << ccount <<endl;
return 0;
}
P7127 「RdOI R1」一次函数(function)
算法标签: 计算几何
注意点: 先解两条直线的交点(-1,-1),然后求面积和S,通过裂项相消的方法得到最终的表达式为
Γ
(
n
)
=
n
2
∗
(
n
+
1
)
,
∀
n
∈
N
\Gamma(n) = \frac{n}{2*(n+1)},\quad\forall n\in\mathbb N
Γ(n)=2∗(n+1)n,∀n∈N,最后输出时注意gcd约分
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
if(b==0){
return a;
}else{
return gcd(b,a%b);
}
}
int main(){
int t;
cin >> t;
while(t--){
int n;
cin >> n;
if(n<1){
printf("0\n");
}else{
int up = n;
int down = 2*(n+1);
int g = gcd(down,up);
up /= g;
down /= g;
printf("%d/%d\n",up,down);
}
}
return 0;
}
P7354 「PMOI-1」骑士の棋
算法标签: 数论
注意点: 找规律,分别讨论以下几种情况:
1.骑士在角上
2.骑士在边上
3.骑士在内部
我们假设国王可以移动的范围为X个,那么所需骑士的数量为
⌈
X
2
⌉
\lceil \frac{X}{2} \rceil
⌈2X⌉个
#include<bits/stdc++.h>
using namespace std;
int xx[] = {1,1,1,0,0,-1,-1,-1};
int yy[] = {1,0,-1,1,-1,1,0,-1};
int main(){
int t;
cin >> t;
while(t--){
int cnt = 0;
int n,m,x,y;
cin >> n >> m >> x >> y;
for(int i=0;i<8;i++){
int tx = xx[i] + x;
int ty = yy[i] + y;
if(tx>=1 && tx<=n && ty>=1 && ty<=m){
cnt++;
}
}
int ans = ceil(1.0*cnt/2.0);
cout << ans << endl;
}
return 0;
}
P1088 [NOIP2004 普及组] 火星人
算法标签: 数论 + 康托展开
#include<bits/stdc++.h>
using namespace std;
int a[10005];
int main(){
int N;
scanf("%d",&N);
int k;
cin >> k;
for(int i=0;i<N;i++){
scanf("%d",&a[i]);
}
while(k--){
next_permutation(a,a+N);
}
for(int i=0;i<N;i++){
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
P1100 高低位交换
算法标签: 数论 + 位运算
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int di = 0xffff;
int gao = 0xffff0000;
unsigned int t1 = (n&di) << 16;
unsigned int t2 = (n&gao) >> 16;
unsigned int ans = t1+t2;
cout << ans << endl;
return 0;
}
P1832 A+B Problem(再升级)
算法标签: 素数 + 背包dp
注意点: 完全背包问题
#include<bits/stdc++.h>
using namespace std;
int a[1005]={1,1,0};
long long dp[1005];
void shai(int n){
for(int i=2;i<=n;i++){
for(int j=2;i*j<=n;j++){
if(!a[i*j]){
a[i*j]=1;
}
}
}
}
int main(){
int n;
cin >> n;
shai(n);
memset(dp,0,sizeof(dp));
dp[0] = 1;
for(int i=2;i<=n;i++){
if(!a[i]){
for(int j=i;j<=n;j++){
dp[j] += dp[j-i];
}
}
}
cout << dp[n] << endl;
return 0;
}
P1009 [NOIP1998 普及组] 阶乘之和
算法标签: 数论 + 高精
注意点: 每一轮迭代都需要进行高精加+高精乘运算
#include<bits/stdc++.h>
using namespace std;
int main(){
int N;
int mul[1000]={1},sum[1000]={0};
cin >> N;
for(int k=1;k<=N;k++){
int jw = 0;
for(int i=0;i<1000;i++){
mul[i] = mul[i] * k + jw ;
jw = mul[i] / 10;
mul[i] = mul[i] % 10;
}
jw= 0 ;
for(int i=0;i<1000;i++){
sum[i] = sum[i] + mul[i] + jw ;
jw = sum[i] / 10;
sum[i] = sum[i] % 10;
}
}
int flag=0;
for(int i=999;i>=0;i--){
if(sum[i]==0){
}else{
flag = 1;
}
if(flag){
cout << sum[i];
}
}
cout << endl;
return 0;
}
P1010 [NOIP1998 普及组] 幂次方
算法标签: 数论 + DFS
注意点: 一道好题!这道题既考察了对数论进制转换的理解,又考察了对DFS的应用,由于答案输出是带有嵌套模式的,因此DFS较为合理,此外,一个数分解成2的幂,等价于将其转换成二进制数,两者结合即可输出结果
#include<bits/stdc++.h>
using namespace std;
string dfs(int n){
int i = 0;
string s = "";
if(n==0){
return string("0");
}
do{
if(n&1){
s =(i==1? "2":"2(" + dfs(i) + ")") + (s==""? "":"+") + s;
}
}while(i++,n>>=1);
return s;
}
int main(){
int n;
cin >> n;
cout << dfs(n) << endl;
return 0;
}
P1062 [NOIP2006 普及组] 数列
算法标签: 数论 + 进制
注意点: k进制转换
#include<bits/stdc++.h>
using namespace std;
long long a[12];
int main(){
memset(a,0,sizeof(a));
int k,N;
cin >> k >> N;
long long tmp = 1;
for(int i=0;i<11;i++){
a[i]=tmp;
tmp*=k;
}
long long ans = 0;
int oct=0;
while(N>0){
if(N%2==1){
ans += a[oct];
}
N/=2;
oct++;
}
cout <<ans << endl;
return 0;
}
P1226 【模板】快速幂||取余运算
算法标签: 数论 + 快速幂
注意点: 快速幂模板
#include<bits/stdc++.h>
using namespace std;
long long b,p,k;
int main(){
cin >> b >> p >> k;
long long b1 = b,p1 = p,k1 = k;
long long ans = 1;
if(k == 1){
printf("%lld^%lld mod %lld=%lld\n",b1,p1,k1,0);
return 0;
}
while(p){
if(p&1){
ans = (ans*b) % k;
}
b = (b*b)%k;
p >>= 1;
}
printf("%lld^%lld mod %lld=%lld\n",b1,p1,k1,ans);
return 0;
}
P1589 泥泞路
算法标签: 数论 + 贪心
注意点: 泥泞路的尾端不计算在覆盖内
#include<bits/stdc++.h>
using namespace std;
struct node{
int s;
int t;
};
node mud[10005];
bool cmp(node m1,node m2){
if(m1.s!=m2.s){
return m1.s < m2.s;
}else{
return m1.t < m2.t;
}
}
int main(){
int n,L;
scanf("%d%d",&n,&L);
for(int i=0;i<n;i++){
scanf("%d%d",&mud[i].s,&mud[i].t);
}
sort(mud,mud+n,cmp);
int start = 0;
int cnt = 0;
for(int i=0;i<n;i++){
if(start<=mud[i].s){
int len = min(mud[n-1].t-1,mud[i].t) - mud[i].s;
int T = (ceil)((len*1.0)/(L*1.0));
cnt += T;
start = mud[i].s + T*L;
}else if(start<=mud[i].t){
int len = min(mud[n-1].t-1,mud[i].t) - start;
int T = (ceil)((len*1.0)/(L*1.0));
cnt += T;
start += T*L;
}
}
cout << cnt << endl;
return 0;
}
P2556 [AHOI2002]黑白图像压缩
算法标签: 数论 + 位运算
注意点: 位运算操作结果保留一位数0或1
#include<bits/stdc++.h>
using namespace std;
int a[10005];
int ans[80005];
int main(){
int n;
cin >> n;
for(int i=0;i<n/8;i++){
cin >> a[i];
}
for(int i=0;i<n/8;i++){
int t = 128;
for(int j=0;j<8;j++){
ans[i*8+j] = (t&a[i])>>(8-j-1);
t >>= 1;
}
}
int start = ans[0];
int cnt = 1;
for(int i=1;i<n;i++){
if(start == ans[i]){
cnt++;
}else{
int res = 0;
if(start == 1){
res += (1<<7);
}
res |= cnt;
printf("%d ",res);
cnt = 1;
start = ans[i];
}
}
if(cnt){
int res = 0;
if(start == 1){
res += (1<<7);
}
res |= cnt;
printf("%d ",res);
}
return 0;
}
P2429 制杖题
算法标签: 数论 + 素数筛
注意点: 素数筛的变式
#include<bits/stdc++.h>
using namespace std;
map<int,int> mp;
const int P = 376544743;
int ans = 0;
int main(){
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++){
int t;
cin >> t;
for(int j=t;j<=m;j+=t){
if(!mp[j]){
mp[j] = 1;
ans += j;
ans %= P;
}
}
}
cout << ans << endl;
return 0;
}
P3913 车的攻击
算法标签: 数论
注意点: map会超时,用unordered_map+O2优化
#include<bits/stdc++.h>
using namespace std;
unordered_map<int,int> mp1;
int rcnt = 0;
unordered_map<int,int> mp2;
int ccnt = 0;
int main(){
int N,K;
cin >> N >> K;
for(int i=0;i<K;i++){
int r,c;
scanf("%d%d",&r,&c);
if(!mp1[r]){
mp1[r] = 1;
rcnt++;
}
if(!mp2[c]){
mp2[c] = 1;
ccnt++;
}
}
long long ans = 1LL*(rcnt+ccnt)*N - 1LL*rcnt*ccnt;
printf("%lld\n",ans);
return 0;
}
P6039 「ACOI2020」音速
算法标签: 计算几何
注意点: 题意描述不是很清楚,可略过此题
#include<bits/stdc++.h>
using namespace std;
int main(){
double x,y,r;
cin >> x >> y >> r;
x = fabs(x);
y = fabs(y);
double ans = 2*r - sqrt(x*x+y*y);
ans = fabs(ans);
printf("%.6lf\n",ans);
if(x!=0 && y==0){
printf("Error\n");
}else{
if(x==0){
printf("0.00\n");
}else{
if(x/y!=0){
printf("%.2lf\n",x/y);
}else{
printf("0.00\n");
}
}
}
return 0;
}
P1170 兔八哥与猎人
算法标签: 数论 + gcd
注意点: 两点之间的连线不经过任何点,要求
Γ
:
g
c
d
(
∣
x
1
−
x
2
∣
,
∣
y
1
−
y
2
∣
)
=
1
\Gamma:gcd(|x1-x2|,|y1-y2|)=1
Γ:gcd(∣x1−x2∣,∣y1−y2∣)=1
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
if(b>a){
swap(a,b);
}
if(b==0){
return a;
}else{
return gcd(b,a%b);
}
}
int main(){
int ax,ay,bx,by;
int n;
cin >> n;
while(n--){
cin >> ax >> ay >> bx >> by;
int g = gcd(abs(ax-bx),abs(ay-by));
if(g==1){
printf("no\n");
}else{
printf("yes\n");
}
}
return 0;
}
P1202 [USACO1.1]黑色星期五Friday the Thirteenth
算法标签: 数论
注意点: 闰年2月有29天
#include<bits/stdc++.h>
using namespace std;
int a[15]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int ans[10];
int islunar(int y){
if((y%4==0 && y%100!=0) || y%400==0){
return 1;
}else{
return 0;
}
}
int main(){
int n;
cin >> n;
int start = 1900;
int t = 13;
for(int i=0;i<n;i++){
int nowy = start + i;
if(islunar(nowy)){
a[2] = 29;
}else{
a[2] = 28;
}
for(int j=1;j<=12;j++){
ans[t%7]++;
t += a[j];
}
}
printf("%d",ans[6]);
for(int i=0;i<6;i++){
printf(" %d",ans[i]);
}
printf("\n");
return 0;
}
P1211 [USACO1.3]牛式 Prime Cryptarithm
算法标签: 数论 + 枚举
注意点: 依次枚举乘数与被乘数,然后检验竖式计算的结果是否符合预期
#include<bits/stdc++.h>
using namespace std;
int a[15];
set<int> s;
int main(){
int n;
cin >> n;
for(int i=0;i<n;i++){
cin >> a[i];
s.insert(a[i]);
}
sort(a,a+n);
int cnt = 0;
if(a[0] == 0){
for(int i=1;i<n;i++){
int ans1 = 0;
ans1 += a[i];
ans1 *= 10;
for(int j=0;j<n;j++){
ans1 += a[j];
ans1 *= 10;
for(int k=0;k<n;k++){
ans1 += a[k];
int ans2 = 0;
for(int p=1;p<n;p++){
ans2 += a[p];
ans2 *= 10;
for(int q=0;q<n;q++){
ans2 += a[q];
int t1 = ans1*a[q];
int t2 = ans1*a[p];
int t = ans1*ans2;
if(t1>=100 && t1<=999 && t2>=100 && t2<=999 && t>=1000 && t<=9999){
bool f = true;
while(f && t1){
if(s.find(t1%10)==s.end()){
f = false;
}
t1/=10;
}
while(f && t2){
if(s.find(t2%10)==s.end()){
f = false;
}
t2/=10;
}
while(f && t){
if(s.find(t%10)==s.end()){
f = false;
}
t/=10;
}
if(f){
cnt++;
}
}
ans2 -= a[q];
}
ans2 /= 10;
ans2 -=a[p];
}
ans1 -= a[k];
}
ans1 /= 10;
ans1 -= a[j];
}
ans1 /= 10;
ans1 -= a[i];
}
}else{
for(int i=0;i<n;i++){
int ans1 = 0;
ans1 += a[i];
ans1 *= 10;
for(int j=0;j<n;j++){
ans1 += a[j];
ans1 *= 10;
for(int k=0;k<n;k++){
ans1 += a[k];
int ans2 = 0;
for(int p=0;p<n;p++){
ans2 += a[p];
ans2 *= 10;
for(int q=0;q<n;q++){
ans2 += a[q];
int t1 = ans1*a[q];
int t2 = ans1*a[p];
int t = ans1*ans2;
if(t1>=100 && t1<=999 && t2>=100 && t2<=999 && t>=1000 && t<=9999){
bool f = true;
while(f && t1){
if(s.find(t1%10)==s.end()){
f = false;
}
t1/=10;
}
while(f && t2){
if(s.find(t2%10)==s.end()){
f = false;
}
t2/=10;
}
while(f && t){
if(s.find(t%10)==s.end()){
f = false;
}
t/=10;
}
if(f){
cnt++;
}
}
ans2 -= a[q];
}
ans2 /= 10;
ans2 -=a[p];
}
ans1 -= a[k];
}
ans1 /= 10;
ans1 -= a[j];
}
ans1 /= 10;
ans1 -= a[i];
}
}
cout << cnt << endl;
return 0;
}
P1258 小车问题
算法标签: 数论 + 方程
注意点: 小学奥数题,列方程
Θ
f
:
(
s
−
2
x
)
+
(
s
−
x
)
b
=
x
a
\Theta f:\frac{(s-2x)+(s-x)}{b} = \frac{x}{a}
Θf:b(s−2x)+(s−x)=ax,解得
x
=
2
a
s
3
a
+
b
x=\frac{2as}{3a+b}
x=3a+b2as,得到总时间为
t
=
x
a
+
s
−
x
b
t=\frac{x}{a}+\frac{s-x}{b}
t=ax+bs−x
#include<bits/stdc++.h>
using namespace std;
int main(){
int s,a,b;
cin >> s >> a >> b;
double x = (2*a*s*1.0)/(3*a+b);
double ans = x/a + (s-x)/b;
printf("%.6lf\n",ans);
return 0;
}
P1293 班级聚会
算法标签: 数论 + 枚举
#include<bits/stdc++.h>
using namespace std;
struct node{
int num;
int dis;
string city;
};
node n[155];
int main(){
int cnt = 0;
int Edis = INT_MAX;
int u = -1;
while(1){
cin >> n[cnt].num >> n[cnt].dis >> n[cnt].city;
cnt++;
if(n[cnt-1].city == "Moscow"){
break;
}
}
for(int i=0;i<cnt;i++){
int ans = 0;
for(int j=0;j<cnt;j++){
if(j!=i){
ans += n[j].num * (abs(n[j].dis-n[i].dis));
}
}
if(ans < Edis){
Edis = ans;
u = i;
}else if(ans == Edis){
if(n[u].dis>n[i].dis){
u = i;
}
}
}
cout << n[u].city << " ";
printf("%d\n",Edis);
return 0;
}
P1093 [NOIP2007 普及组] 奖学金
算法标签: 数论 + 排序
#include<bits/stdc++.h>
using namespace std;
typedef struct award{
int No;
int score1;
int score2;
int score3;
int total;
}student;
student stu[301];
bool cmp(student stu1,student stu2){
if(stu1.total<stu2.total){
return false;
}else if(stu1.total==stu2.total){
if(stu1.score1>stu2.score1){
return true;
}else if(stu1.score1==stu2.score1){
if(stu1.No<stu2.No){
return true;
}else{
return false;
}
}else{
return false;
}
}else{
return true;
}
}
int main(){
int N;
cin >> N;
int a,b,c;
for(int i=0;i<N;i++){
cin >> a >> b >> c;
stu[i].No=i+1;
stu[i].score1=a;
stu[i].score2=b;
stu[i].score3=c;
stu[i].total=a+b+c;
}
sort(stu,stu+N,cmp);
for(int i=0;i<5;i++){
cout << stu[i].No << " " <<stu[i].total << endl;
}
return 0;
}
P3383 【模板】线性筛素数
算法标签: 数论 + 素数筛
注意点: 素数筛+快读模板题,复杂度
O
(
n
l
o
g
n
)
\ O(nlogn)
O(nlogn)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e7;
int p[maxn+5];
bool vis[maxn*10+5];
int cnt = 0;
int n,q;
inline int read(){
int f = 1, val = 0; char ch = getchar();
while ((ch < '0' || ch > '9') && (ch != '-')) ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') val = (val << 3) + (val << 1) + ch - '0', ch = getchar();
return val * f;
}
void prime(int n){
for(register int i=2;i<=n;i++){
if(!vis[i]){
p[cnt++] = i;
}
for(register int j=0;j<cnt && i*p[j]<=n;j++){
vis[i*p[j]] = 1;
if(i%p[j]==0){
break;
}
}
}
}
int main(){
n = read();
vis[1] = 1;
prime(n);
q = read();
for(register int i=0;i<q;i++){
int k;
k = read();
printf("%d\n",p[k-1]);
}
return 0;
}
P3152 正整数序列
算法标签: 数论 + 分治
注意点: 我们可以将序列一分为二,每次变换较大的序列,使之成为较小序列的一部分,这样只需要
l
o
g
2
(
n
)
+
1
\ log2(n)+1
log2(n)+1次变换即可
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int ans = (floor)(log2(n))+1;
cout << ans << endl;
return 0;
}
P4057 [Code+#1]晨跑
算法标签: 数论 + lcm
注意点: 最小公倍数模板题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b){
return b==0 ? a : gcd(b,a%b) ;
}
ll lcm(ll a,ll b){
if(a<b){
swap(a,b);
}
ll tmp = gcd(a,b);
return 1LL*(a)*(b/tmp);
}
int main(){
ll a,b,c;
cin >> a >> b >> c;
ll ans = lcm(a,b);
ans = lcm(ans,c);
cout << ans << endl;
return 0;
}
P5990 [PA2015]Kieszonkowe
算法标签: 数论
注意点: 无解的情况只能是当偶数一个都没有,而奇数恰好有一个的时候,此时无论如何整数和都不可能是偶数
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int odd[maxn];
int even[maxn];
int cnt1 = 0;
int cnt2 = 0;
int main(){
int n;
cin >> n;
for(int i=0;i<n;i++){
int t;
cin >> t;
if(t%2){
odd[cnt1++] = t;
}else{
even[cnt2++] = t;
}
}
sort(odd,odd+cnt1);
if(cnt2==0 && cnt1==1){
cout << "NIESTETY" << endl;
return 0;
}
int ans = 0;
for(int i=0;i<cnt2;i++){
ans += even[i];
}
if(cnt1%2){
for(int i=cnt1-1;i>=1;i--){
ans += odd[i];
}
}else{
for(int i=cnt1-1;i>=0;i--){
ans += odd[i];
}
}
cout << ans << endl;
return 0;
}
P6546 [COCI2010-2011#2] PUŽ
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,v;
cin >> a >> b >> v;
int ans = (ceil)((v-a)*1.0/(a-b));
ans += 1;
cout << ans << endl;
return 0;
}
P6188 [NOI Online #1 入门组] 文具订购
算法标签: 数论
注意点: 依次枚举0-13所有可能的情况,逐一判断最佳的购买方案数!
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int a=0,b=0,c=0;
if(n==0){
cout << "0 0 0" << endl;
return 0;
}else if(n<=2 || n==5){
cout << "-1" << endl;
return 0;
}
int ans = n/14;
int now = n%14;
a = b = c = ans;
if(now == 1){
a--;
b--;
c+=4;
}else if(now == 2){
a--;
c+=3;
}else if(now == 3){
c++;
}else if(now == 4){
b++;
}else if(now == 5){
a--;
c+=4;
}else if(now == 6){
c+=2;
}else if(now == 7){
b+=1;
c+=1;
}else if(now == 8){
b+=2;
}else if(now == 9){
c+=3;
}else if(now == 10){
b+=1;
c+=2;
}else if(now == 11){
b+=2;
c+=1;
}else if(now == 12){
c+=4;
}else if(now == 13){
b+=1;
c+=3;
}
cout << a << " " << b << " " << c << endl;
return 0;
}
P6746 『MdOI R3』Operations
算法标签: 数论
注意点: 一道好题!分4种情况进行讨论:
1.a=0 且 b=0,此时的代价为0
2.a=0 或 b=0,此时的代价为d,即
0
∗
A
=
0
,
A
A
+
1
=
0
\ 0*A=0,\frac{A}{A+1}=0
0∗A=0,A+1A=0
3.a=b时,代价为c或者2*d的最小值,原因是
A
A
+
1
=
0
,
B
B
+
1
=
0
,
\frac{A}{A+1}=0,\frac{B}{B+1}=0,
A+1A=0,B+1B=0,两次操作等价于一次操作
4.
a
!
=
b
\ a!=b
a!=b的时候,取
c
+
d
\ c+d
c+d和
2
∗
d
\ 2*d
2∗d的较小值
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
LL a,b,c,d;
cin >> a >> b >> c >> d;
if(a==0 && b==0){
cout << "0" << endl;
}else if(a==0 || b==0){
cout << d << endl;
}else if(a==b){
cout << min(c,2*d) << endl;
}else{
cout << min(c+d,2*d) << endl;
}
return 0;
}
P5514 [MtOI2019]永夜的报应
算法标签: 数论
注意点: 一道好题!事实上无需分组,直接求异或和即为最小!
a
⊕
b
<
=
a
+
b
\ a \oplus b<=a+b
a⊕b<=a+b
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int a[maxn];
int main(){
int n;
cin >> n;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int ans = 0;
for(int i=0;i<n;i++){
ans ^= a[i];
}
cout << ans << endl;
return 0;
}
P5657 [CSP-S2019] 格雷码
算法标签: 数论 + DFS
注意点: 一道好题!格雷码+DFS,需注意:
1.“倒序”指的是n-1位格雷码在序列中的位置{从后向前数),而不是指n-1位格雷码倒过来拼接上“0”或“1”
2.不要使用pow函数,因为精度不够,自定义一个函数power
3.注意数据范围最大为64位,需用unsigned long long
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
LL power(LL n){
LL ans = 1;
for(LL i=1;i<=n;i++){
ans *= 2;
}
return ans;
}
string dfs(LL n,LL k){
if(n==1){
if(k==0){
return string("0");
}else if(k==1){
return string("1");
}
}
if(k>=power(n-1)){
string s = dfs(n-1,power(n)-k-1);
return "1" + s;
}else{
return "0" + dfs(n-1,k);
}
}
int main(){
LL n,k;
cin >> n >> k;
string s = dfs(n,k);
cout << s << endl;
return 0;
}
P1287 盒子与球
算法标签: 数论
注意点: Stirling数模板题
#include<bits/stdc++.h>
using namespace std;
int stirling(int n,int m){
if(n<m || m<=0){
return 0;
}else if(n == m){
return 1;
}else{
return m*stirling(n-1,m) + stirling(n-1,m-1);
}
}
int main(){
int n,r;
cin >> n >> r;
int ans = stirling(n,r);
for(int i=1;i<=r;i++){
ans *= i;
}
cout << ans << endl;
return 0;
}
P1932 A+B A-B A*B A/B A%B Problem
算法标签: 数论 + 高精
注意点: 一道高精算法的经典题!本题是高精度算法的大集合,包含了高精加、高精减、高精乘、高精除、高精模除,需仔细掌握高精算法的原理,并注意实现的细节!
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e4+5;
int a[maxn];
int b[maxn];
int pplus[maxn];
int mminus[maxn];
int muti[maxn];
int Ta[maxn];
int Tb[maxn];
int divide[maxn];
string s1,s2;
void init(){
cin >> s1 >> s2;
int len1 = s1.size();
int len2 = s2.size();
for(int i=0;i<len1;i++){
a[len1-i-1] = s1[i] - '0';
}
for(int i=0;i<len2;i++){
b[len2-i-1] = s2[i] - '0';
}
}
void add(int a[],int b[],int pplus[]){
int jw = 0;
int len1 = s1.size();
int len2 = s2.size();
for(int i=0;i<max(len1,len2);i++){
pplus[i] = a[i] + b[i] + jw;
jw = pplus[i] / 10;
pplus[i] %= 10;
}
if(jw){
pplus[max(len1,len2)] = jw;
for(int i=max(len1,len2);i>=0;i--){
printf("%d",pplus[i]);
}
printf("\n");
}else{
for(int i=max(len1,len2)-1;i>=0;i--){
printf("%d",pplus[i]);
}
printf("\n");
}
}
void minu(int a[],int b[],int mminus[]){
bool f_minus = true;
int len1 = s1.size();
int len2 = s2.size();
if(len2>len1){
f_minus = false;
}else if(len2 == len1){
for(int i=len2-1;i>=0;i--){
if(a[i]<b[i]){
f_minus = false;
break;
}else if(a[i]>b[i]){
break;
}
}
}
if(f_minus){
for(int i=0;i<=max(len1,len2)-1;i++){
mminus[i] = mminus[i] + a[i] - b[i];
if(mminus[i]<0){
mminus[i] += 10;
mminus[i+1]--;
}
}
}else{
for(int i=0;i<=max(len1,len2)-1;i++){
mminus[i] = mminus[i] + b[i] - a[i];
if(mminus[i]<0){
mminus[i] += 10;
mminus[i+1]--;
}
}
}
int cnt = max(len1,len2);
for(int i=cnt-1;i>=0;i--){
if(mminus[i] == 0){
cnt--;
}else{
break;
}
}
if(!cnt){
printf("0");
}else{
if(!f_minus){
printf("-");
}
for(int i=cnt-1;i>=0;i--){
printf("%d",mminus[i]);
}
}
printf("\n");
}
void mutiply(int a[],int b[],int muti[]){
int len1 = s1.size();
int len2 = s2.size();
for(int i=0;i<len1;i++){
for(int j=0;j<len2;j++){
muti[i+j] += a[i]*b[j];
}
}
int jw = 0;
for(int i=0;i<len1+len2-1;i++){
muti[i] = muti[i] + jw;
jw = muti[i]/10;
muti[i] %= 10;
}
int cnt = 0;
while(jw){
muti[len1+len2+cnt-1] = jw%10;
jw/=10;
cnt++;
}
for(int i=len1+len2+cnt-2;i>=0;i--){
printf("%d",muti[i]);
}
printf("\n");
}
int compare(int a[], int b[]) {
if(a[0]>b[0]){
return 1;
}else if(a[0]<b[0]){
return -1;
}
for (int i=a[0];i>0;i--){
if (a[i]>b[i]){
return 1;
}else if(a[i]<b[i]){
return -1;
}
}
return 0;
}
void shift(int a[],int b[],int dis){
for (int i=1;i<=a[0];i++){
b[i+dis-1]=a[i];
}
b[0]=a[0]+dis-1;
}
void dividen(int a[],int b[],int divide[]){
int len1 = s1.size();
int len2 = s2.size();
Ta[0] = len1;
for(int i=0;i<len1;i++){
Ta[len1-i] = s1[i]-'0';
}
Tb[0] = len2;
for(int i=0;i<len2;i++){
Tb[len2-i] = s2[i]-'0';
}
if(0==compare(Ta,Tb)){
printf("1\n0\n");
}else if (-1==compare(Ta,Tb)) {
printf("0\n");
cout << s1 << endl;
}else {
int tmp[maxn];
divide[0] = Ta[0]-Tb[0]+1;
for(int i=divide[0];i>0;i--) {
memset(tmp,0,sizeof(tmp));
shift(Tb,tmp,i);
while(compare(Ta,tmp)>=0) {
divide[i]++;
for(int j=1;j<=Ta[0];j++){
if(Ta[j]<tmp[j]) {
Ta[j+1]--;
Ta[j] += 10;
}
Ta[j] -= tmp[j];
}
int k = Ta[0];
while(Ta[k]==0) {
k--;
}
Ta[0]=k;
}
}
while (divide[0]>0 && divide[divide[0]]==0) {
divide[0]--;
}
for (int i=divide[0]; i>0; i--) {
printf("%d",divide[i]);
}
printf("\n");
if(0==Ta[0]) {
printf("0\n");
}else{
for(int i=Ta[0];i>0;i--){
printf("%d",Ta[i]);
}
printf("\n");
}
}
}
int main(){
init();
add(a,b,pplus);
minu(a,b,mminus);
mutiply(a,b,muti);
dividen(a,b,divide);
return 0;
}
P1327 数列排序
算法标签: 数论 + 排序
注意点: 和PAT甲级 1067题类似,不同点在于,本题的数列值可以是负数,且交换时不一定和0交换(交换空间任意)
因此我们需要map来记录原数组的下标,交换数组时map里面的值也需要交换(更新)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
int b[maxn];
map<int,int> mp;
int main(){
int N;
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&a[i]);
mp[a[i]] = i;
b[i] = a[i];
}
sort(b,b+N);
int cnt = 0;
for(int i=0;i<N;i++){
if(a[i]!=b[i]){
int t = mp[b[i]];
mp[a[i]] = t;
mp[b[i]] = i;
swap(a[t],a[i]);
cnt++;
}
}
cout << cnt << endl;
return 0;
}
P5436 【XR-2】缘分
算法标签: 数论
注意点: 拥有最大最小公倍数的数字,一定是相邻的两项!(PAT 乙级 P1104与之类似)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b){
if(b==0){
return a;
}else{
return gcd(b,a%b);
}
}
int main(){
int T;
cin >> T;
while(T--){
LL n;
cin >> n;
if(n==1){
cout << "1" << endl;
}else{
LL ans = 1;
for(LL i=n;i>=1;i--){
if(gcd(i,i-1)==1){
ans = i*(i-1);
break;
}
}
cout << ans << endl;
}
}
return 0;
}
P1358 扑克牌
算法标签: 数论 + 排列组合
注意点: 组合数问题
#include<bits/stdc++.h>
using namespace std;
const int P = 10007;
int dp[10005][105] ={0};
int a[105];
int main(){
int n,m;
cin >> n >> m;
dp[1][0] = 1;
dp[1][1] = 1;
for(int i=2;i<=n;i++){
for(int j=0;j<=min(i,100);j++){
if(j==0 || j==i){
dp[i][j] = 1;
}else{
dp[i][j] = (dp[i-1][j] + dp[i-1][j-1]) % P;
}
}
}
for(int i=1;i<=m;i++){
cin >> a[i];
}
int ans = 1;
for(int i=1;i<=m;i++){
ans *= dp[n][a[i]];
ans %= P;
n -= a[i];
}
cout << ans << endl;
return 0;
}
P3984 高兴的津津
算法标签: 数论
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long LL;
int a[maxn];
LL cnt = 0;
int main(){
int n;
LL T;
cin >> n >> T;
for(int i=0;i<n;i++){
cin >> a[i];
}
LL s = a[0];
for(int i=0;i<n;i++){
if(s>a[i]){
cnt += (a[i] + T - s);
s = a[i] + T;
}else{
cnt += T;
s = a[i] + T;
}
}
cout << cnt << endl;
return 0;
}
P1067 [NOIP2009 普及组] 多项式输出
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[102];
int N;
cin >> N;
int p = N;
for(int i=0;i<=N;i++){
cin >> a[i];
}
int i=0;
int flag=0;
for(int i=0;i<=N;i++){
while(!a[i] && !flag){
i++;
p--;
}
if(p==1){
if(!flag){
}else{
if(a[i]>0){
cout << "+" ;
}
}
if(a[i]>1){
cout << a[i] << "x";
}
if(a[i]==1){
cout << "x";
}
if(a[i]==-1){
cout << "-x";
}
if(a[i]<-1){
cout << a[i] << "x";
}
}else if(p>1){
if(!flag){
}else{
if(a[i]>0){
cout << "+" ;
}
}
if(a[i]>1){
cout << a[i] << "x^" << p;
}
if(a[i]==1){
cout << "x^" << p;
}
if(a[i]==-1){
cout << "-x^" << p;
}
if(a[i]<-1){
cout << a[i] << "x^" << p;
}
}else if(p==0){
if(!flag){
}else{
if(a[i]>0){
cout << "+" << a[i];
}else if(a[i]<0){
cout << a[i];
}
}
}
flag = 1;
p--;
}
return 0;
}
P1147 连续自然数和
算法标签: 数论
注意点: 高斯求和
#include<bits/stdc++.h>
using namespace std;
int main(){
int M;
scanf("%d",&M);
for(int k1 =sqrt(2*M);k1>1;k1--){
if(2*M % k1 == 0){
int k2 = 2*M / k1;
if((k1+k2)%2==1){
printf("%d %d\n",(k2-k1+1)/2,(k1+k2-1)/2);
}
}
}
return 0;
}
P1866 编号
算法标签: 数论 + 排序
注意点: 组合取数问题
#include<bits/stdc++.h>
using namespace std;
const long long mod = 1000000007;
long long ans = 1;
int b[100];
int main(){
int N;
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&b[i]);
}
sort(b,b+N);
for(int i=0;i<N;i++){
ans = (ans*(b[i]-i)) % mod;
}
printf("%lld\n",ans);
return 0;
}
P2640 神秘磁石
算法标签: 数论 + 素数筛
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
bool a[maxn] = {1,1,0};
void init(){
for(int i=2;i<maxn;i++){
if(!a[i]){
for(int j=2*i;j<maxn;j+=i){
a[j] = 1;
}
}
}
}
int main(){
init();
int n,k;
cin >> n >> k;
int cnt = 0;
for(int i=2;i<=n-k;i++){
if(!a[i] && !a[i+k]){
printf("%d %d\n",i,i+k);
cnt++;
}
}
if(!cnt){
printf("empty\n");
}
return 0;
}
P4122 [USACO17DEC]Blocked Billboard B
算法标签: 数论 + 模拟
注意点: 一道好题,以下给出的是
O
(
1
)
\ O(1)
O(1)的算法
#include<bits/stdc++.h>
using namespace std;
int a[5];
int b[5][3];
int main(){
int x1,y1,x2,y2;
int x3,y3,x4,y4;
int x5,y5,x6,y6;
cin >> x1 >> y1 >> x2 >> y2
>> x3 >> y3 >> x4 >> y4
>> x5 >> y5 >> x6 >> y6;
int ans = abs(x1-x2)*abs(y1-y2) + abs(x3-x4)*abs(y3-y4);
int t1 = max(0,min(x2,x6)-max(x1,x5))*max(0,min(y2,y6)-max(y1,y5));
int t2 = max(0,min(x4,x6)-max(x3,x5))*max(0,min(y4,y6)-max(y3,y5));
ans -= t1;
ans -= t2;
cout << ans << endl;
return 0;
}
P1011 [NOIP1998 提高组] 车站
算法标签: 数论 + Fibonacci数列
#include<bits/stdc++.h>
using namespace std;
int a[21];
void fibo(int a[]){
a[1]=1;
a[2]=1;
for(int i=3;i<=20;i++){
a[i]=a[i-1]+a[i-2];
}
}
int main(){
int a1,n,m,x1;
cin >> a1 >> n >> m >> x1;
fibo(a);
if(n==1||n==2||n==3){
cout << a << endl;
return 0;
}
if(x1==n){
cout <<"0"<<endl;
}
int tmpa = 2;
for(int i=1;i<=n-5;i++){
tmpa = tmpa +a[i];
}
int tmpx = 0;
for(int i=1;i<=n-4;i++){
tmpx = tmpx +a[i];
}
m = m -tmpa*a1;
int x;
x = m / tmpx;
tmpa = 2;
for(int i=1;i<=x1-4;i++){
tmpa =tmpa+a[i];
}
tmpx=0;
for(int i=1;i<=x1-3;i++){
tmpx = tmpx +a[i];
}
int ans = a1*tmpa +x*tmpx;
cout << ans << endl;
return 0;
}
P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib
算法标签: 数论 + DFS
注意点: 每层DFS相当于多了一个数位
#include<bits/stdc++.h>
using namespace std;
int ans[10005];
int n;
int isprime(int n){
if(n<2){
return 0;
}
for(int i=2;i<=sqrt(n);i++){
if(n % i==0){
return 0;
}
}
return 1;
}
int cnt = 0;
void dfs(int depth,int num){
if(depth == n){
if(isprime(num)){
ans[cnt++] = num;
}
return;
}
for(int i=1;i<=9;i++){
if(isprime(num*10+i)){
dfs(depth+1,num*10+i);
}
}
}
int main(){
cin >> n;
dfs(0,0);
sort(ans,ans+cnt);
for(int i=0;i<cnt;i++){
printf("%d\n",ans[i]);
}
return 0;
}
P1102 A-B 数对
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn];
int main(){
int N,C;
long long ans = 0;
scanf("%d%d",&N,&C);
for(int i=0;i<N;i++){
scanf("%d",&a[i]);
}
sort(a,a+N);
int i = 0,j = 0;
long long count = 1;
while(i<=j && j<=N){
if(a[j]-a[i]>C){
i++;
}else if(a[j]-a[i] == C){
if(a[j] == a[j+1]){
count++;
j++;
}else{
if(a[i] == a[i+1]){
ans += count;
i++;
}else{
ans += count;
i++;
count =1;
}
}
}else{
j++;
}
}
printf("%lld\n",ans);
return 0;
}
P1326 足球
算法标签: 数论
注意点:
一道好题!这道题反复提交了十多次才通过,认真总结了一下本题的思路:
我们分别对最大得分和最小得分进行讨论——
最大得分:
1.当进球数
S
>
=
N
−
1
\ S>=N-1
S>=N−1时,我们将前N-1场比赛都假设成赢,那么得分将有
3
∗
(
N
−
1
)
\ 3*(N-1)
3∗(N−1),比较最后一场比赛,若S>T,得3分,S=T得2分,否则得0分
2.当进球数
S
<
N
−
1
\ S<N-1
S<N−1时,必须输一局,争取和局最多,此时,安排S场赢,1场输,剩下全和,这样总得分最高
最小得分:
1.当
S
>
T
S>T
S>T时,必须赢一局,因此最小得分的情况是赢一局,输T局(不超过N),剩下全和
2.当
S
<
=
T
S<=T
S<=T时,有两种可能的情形:
2-1 必须赢一局,因此最小得分的情况是赢一局,输T局(不超过N),剩下全和
2-2 一局都没赢,尽最大可能输,剩余和
最小得分取两种情形下的较小者
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
LL S,T,N;
while(cin >> S >> T >> N){
LL minn=0;
LL maxn=0;
if(S>=N-1){
maxn = 3*(N-1);
if(S-(N-1)==T){
maxn += 1;
}else if(S-(N-1)>T){
maxn += 3;
}else{
maxn += 0;
}
}else{
maxn = 3*S + (N-1-S)*1;
if(T == 0){
maxn += 1;
}
}
if(S>T){
minn = 3 + 0LL*min(T,N) + 1*max(0LL,N-1-T);
}else{
LL t = 3 + 0LL*min(T,N) + 1*max(0LL,N-1-T);
minn = min(max(N-(T-S),0LL)*1,t);
}
cout << maxn << " " << minn << endl;
}
return 0;
}
P1403 [AHOI2005]约数研究
算法标签: 数论
注意点: 求
1
−
n
\ 1-n
1−n所有正整数的约数的个数,等价于求正整数i的因子个数之和,因此只要累加
N
i
\frac{N}{i}
iN即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans = 0;
int main(){
int N;
cin >> N;
for(int i=1;i<=N;i++){
ans += N/i;
}
cout << ans << endl;
return 0;
}
P1604 B进制星球
算法标签: 数论 + 进制 + 高精
#include<bits/stdc++.h>
using namespace std;
int a[2005];
int b[2005];
int ans[2005];
int main(){
int B;
cin >> B;
string s1,s2;
cin >> s1 >> s2;
int len1 = s1.size();
int len2 = s2.size();
for(int i=0;i<len1;i++){
if(s1[i]>='A'){
a[len1-i-1] = s1[i]-'A'+10;
}else{
a[len1-i-1] = s1[i]-'0';
}
}
for(int i=0;i<len2;i++){
if(s2[i]>='A'){
b[len2-i-1] = s2[i]-'A'+10;
}else{
b[len2-i-1] = s2[i]-'0';
}
}
int jw = 0;
for(int i=0;i<max(len1,len2);i++){
ans[i] = jw + a[i] + b[i];
jw = ans[i] / B;
ans[i] %= B;
}
if(jw){
ans[max(len1,len2)] = jw;
for(int i=max(len1,len2);i>=0;i--){
if(ans[i]>=10){
printf("%c",ans[i]-10+'A');
}else{
printf("%c",ans[i]+'0');
}
}
}else{
for(int i=max(len1,len2)-1;i>=0;i--){
if(ans[i]>=10){
printf("%c",ans[i]-10+'A');
}else{
printf("%c",ans[i]+'0');
}
}
}
return 0;
}
P1348 Couple number
算法标签: 数论 + 枚举
注意点: 首先,我们观察平方数1,4,9,16…,它们两两之间的差为奇数1,3,5,7…,这意味着所有的奇数都可以表达成两个平方数之差,接下来我们考察因式分解下
(
x
+
y
)
(
x
−
y
)
,
(
x
+
y
)
\ (x+y)(x-y),(x+y)
(x+y)(x−y),(x+y)与
(
x
−
y
)
\ (x-y)
(x−y)具有相同的奇偶性,由于之前讨论的是奇数的情形,那么现在讨论偶数情况下,两个偶数相乘必为4的倍数,因此所有是4的倍数的数字也符合情况,最后枚举输出结果
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e7;
bool hashT[2*maxn+5];
int main(){
int a,b;
cin >> a >> b;
int cnt = 0;
for(int i=a;i<=b;i++){
if(abs(i)%4 == 0 || abs(i)%2==1){
cnt++;
}
}
cout << cnt << endl;
return 0;
}
P1855 榨取kkksc03
算法标签: 动态规划 + 背包
注意点: 三维背包
#include<bits/stdc++.h>
using namespace std;
int dp[201][201];
int m[201];
int t[201];
int main(){
int n,M,T;
cin>>n >>M >>T;
for(int i=0;i<n;i++){
cin >> m[i];
cin >> t[i];
}
dp[0][0]=0;
for(int i=0;i<n;i++){
for(int j=M;j>=m[i];j--){
for(int k=T;k>=t[i];k--){
dp[j][k]=max(dp[j][k],dp[j-m[i]][k-t[i]]+1);
}
}
}
cout << dp[M][T] << endl;
return 0;
}
P1679 神奇的四次方数
算法标签: 数论 + 背包
注意点: 本题既可以用动态规划的方法来完成,也可以采用DFS数字搜索来完成
#include<bits/stdc++.h>
using namespace std;
int a[18];
int dp[100001];
int main(){
memset(dp,0x3f3f3f3f,sizeof(dp));
int m;
cin >> m;
for(int i=1;i<=17;i++){
int tmp=1;
for(int j=0;j<4;j++){
tmp = tmp * i;
}
a[i]=tmp;
}
dp[0]=0;
for(int i=1;i<=17;i++){
for(int j=a[i];j<=m;j++){
dp[j]=min(dp[j],dp[j-a[i]]+1);
}
}
cout << dp[m] << endl;
return 0;
}
P1889 士兵站队
算法标签: 数论 + 中位数
注意点: 这道题考察的是对中位数的理解,中位数必须满足所有的点到中点的距离之和最短,这正好符合题意(移动的步数最少)
#include<bits/stdc++.h>
using namespace std;
int x[10005];
int y[10005];
int main(){
int n;
cin >> n;
for(int i=1;i<=n;i++){
cin >> x[i] >> y[i];
}
sort(x+1,x+n+1);
for(int i=1;i<=n;i++){
x[i] -= i;
}
sort(x+1,x+n+1);
sort(y+1,y+n+1);
int ans1,ans2;
if(n%2==0){
ans1 = (x[n/2]+x[n/2+1])/2;
ans2 = (y[n/2]+y[n/2+1])/2;
}else{
ans1 = x[n/2+1];
ans2 = y[n/2+1];
}
long long cnt = 0;
for(int i=1;i<=n;i++){
cnt += (abs(x[i]-ans1));
cnt += (abs(y[i]-ans2));
}
cout << cnt << endl;
return 0;
}
P1706 全排列问题
算法标签: 数论 + DFS
注意点: 深入一层,就枚举一个未被选过的数,加入数组中,直到层数=N,搜索完毕,输出结果
#include<bits/stdc++.h>
using namespace std;
int N;
int a[15];
bool hashT[15];
void dfs(int depth){
if(depth == N){
for(int i=0;i<N;i++){
cout << setw(5) << a[i];
}
printf("\n");
}else{
for(int i=1;i<=N;i++){
if(!hashT[i]){
hashT[i] = true;
a[depth] = i;
dfs(depth+1);
hashT[i] = false;
}
}
}
}
int main(){
cin >> N;
dfs(0);
return 0;
}
P2118 [NOIP2014 普及组] 比例简化
算法标签: 数论 + 枚举
注意点: 从1-L分别枚举A’和 B’,使得新的分数比之前大且两数之差最小
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
LL A,B,L;
LL u,v;
cin >> A >> B >> L;
LL minn = LONG_MAX;
for(int i=1;i<=L;i++){
for(int j=1;j<=L;j++){
LL t1 = 1LL*A*j;
LL t2 = 1LL*B*i;
if(t1<=t2 && t2-t1<minn){
minn = t2-t1;
u = i;
v = j;
}
}
}
LL g = __gcd(u,v);
u /= g;
v /= g;
cout << u << " " << v << endl;
return 0;
}
P2719 搞笑世界杯
算法标签: 数论 + 动态规划
注意点: dp[i][j]表示有i个A,j个B时最后2个相同的概率。
#include<bits/stdc++.h>
using namespace std;
double f[1255][1255];
int main(){
int n;
cin >> n;
n = n/2;
for(int i=2;i<=n;i++){
f[i][0] = f[0][i] = 1.0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
f[i][j] = (f[i-1][j] + f[i][j-1])/2.0;
}
}
printf("%.4lf\n",f[n][n]);
return 0;
}
P2651 添加括号III
算法标签: 数论
注意点:
a
[
2
]
\ a[2]
a[2]一定在分母,
a
[
1
]
、
a
[
3
]
.
.
.
a
[
n
]
\ a[1]、a[3]...a[n]
a[1]、a[3]...a[n]一定在分子,因此只要判断
a
[
2
]
\ a[2]
a[2]能否被其余数整除即可
#include<bits/stdc++.h>
using namespace std;
int a[10005];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int t = a[1];
bool flag = false;
for(int i=0;i<n;i++){
if(i!=1){
int g = __gcd(a[i],t);
t /= g;
}
if(t==1){
flag = true;
break;
}
}
if(flag){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}
P2660 zzc 种田
算法标签: 数论 + gcd
注意点: 每次种最大的正方形,因此不断辗转相除,直到最后一块地为正方形为止
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans = 0;
int main(){
ll a,b;
cin >> a >> b;
ll ans = 0;
if(a<b){
swap(a,b);
}
while(a%b){
ans += (a/b) *b*4;
ll tmp = b;
b = a%b;
a = tmp;
}
ans += 1LL*a*4;
cout << ans <<endl;
return 0;
}
P5834 [USACO19DEC]MooBuzz S
算法标签: 数论 + 模拟
#include<bits/stdc++.h>
using namespace std;
int a[]={0,1,2,4,7,8,11,13,14};
int main(){
long long N;
cin >> N;
long long ans1 = N/8;
long long ans2 = N%8;
ans1 *= 15;
if(ans2 == 0){
cout << ans1-1 << endl;
}else{
long long ans = 0;
ans += ans1;
ans += a[ans2];
cout << ans << endl;
}
return 0;
}
P6068 『MdOI R1』GCD? GCD!
算法标签: 数论 + 素数筛
#include<bits/stdc++.h>
using namespace std;
int main(){
int T;
cin >> T;
while(T--){
int n;
cin >> n;
int u = -1;
for(int i=1;i<=sqrt(n);i++){
if(n%i==0){
if(i>=6){
u = max(u,n/i);
}else if(n/i>=6){
u = max(i,u);
}
}
}
cout << u << endl;
}
return 0;
}
P1163 银行贷款
算法标签: 数论 + 二分
注意点: 二分利率,计算还款数额与题目所给数值之差是否在0.001内
#include<bits/stdc++.h>
using namespace std;
int n,x,m;
double S;
double calcu(double r){
double ans = n*1.0;
for(int i=1;i<=m;i++){
ans *= (1.0+r);
ans = ans - x;
}
return ans;
}
double binary(double left,double right){
if(fabs(right-left)<1e-5){
return left;
}else{
double mid = (left+right)/2.0;
double res = calcu(mid);
if(res<=0){
return binary(mid,right);
}else{
return binary(left,mid);
}
}
}
int main(){
cin >> n >> x >> m;
S = (x*m*1.0)/(n*1.0);
double ans = binary(0.0,3.0);
ans *= 100.0;
printf("%.1lf\n",ans);
return 0;
}