链接:http://vjudge.net/contest/133425#overview
A.
题意:T组数据,n个数,找到每个数对应的映射满足f(x) >= n,(x最小)f表示欧拉函数,即小于x且与x互素的个数。将x求和,使得sum最小。
解析:首先需要欧拉函数。方法一:然后二分搜索(错误or处理一下遍历一遍将当前点 phi[i] = max(phi[i - 1],phi[i])即可)。方法二:另一种是打表,将每个不同的值中对应的最小x都找出来。每次都更新一段,从上一次更新结束的右一位开始到本次phi[i]结束。都要等于i。保证re[]对应的都是最小的值
代码:
方法一:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 1100000;
int phi[maxn + 5];
void phi_table(int n)
{
for(int i = 2; i <= n; i ++)
phi[i] = 0;
phi[1] = 0;
for(int i = 2; i <= n; i ++)
{
if(!phi[i])
{
for(int j = i; j <= n; j += i)
{
if(!phi[j])
{
phi[j] = j;
}
phi[j] = phi[j] / i * (i - 1);
}
}
}
for(int i = 1; i <= n; i ++)
{
phi[i] = max(phi[i - 1],phi[i]);
}
}
int main()
{
//freopen("in.txt","r",stdin);
phi_table(maxn);
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
long long ans = 0;
int n,x;
scanf("%d",&n);
for(int i = 0; i < n; i++)
{
scanf("%d",&x);
ans += lower_bound(phi,phi + maxn,x) - phi;
}
printf("Case %d: %lld Xukha\n",t,ans);
}
return 0;
}
方法二:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 1100000;
int phi[maxn + 5];
int re[maxn];
void phi_table(int n)
{
for(int i = 2; i <= n; i ++)
phi[i] = 0;
phi[1] = 0;
for(int i = 2; i <= n; i ++)
{
if(!phi[i])
{
for(int j = i; j <= n; j += i)
{
if(!phi[j])
{
phi[j] = j;
}
phi[j] = phi[j] / i * (i - 1);
}
}
}
}
void fun()
{
//only uese the minx places that have been before (it means that with smaller money to get these score)
int minx = 0;
for(int i = 0; i < maxn; i ++)
{
if(re[phi[i]] == 0)
{
for(int j = minx + 1; j <= phi[i] && j <= 1000000; j ++)
{
re[j] = i;
}
minx = phi[i];
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
phi_table(maxn);
fun();
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
long long ans = 0;//pay attention to the long long
int n,x;
scanf("%d",&n);
for(int i = 0; i < n; i++)
{
scanf("%d",&x);
ans += re[x];
}
printf("Case %d: %lld Xukha\n",t,ans);
}
return 0;
}
B.
题意:给一个集合,求一个子集。使得子集中的每个元素都不能由其他元素乘以一个素数得到。求子集最大的个数。
分析:每个数所有指数的和为奇数或偶数。这样一个二分图。只有指数和为奇数的,才能到达偶数的。偶数的才能到达奇数的。首先分解质因数。并分类存储他们的坐标。以偶数的出发对奇数的建边,每次除以一个出现的素数。以奇数的出发,除以一个素数对偶数的建边。保证不重复不漏的简历一个有向的二分图。然后二分匹配求最大匹配。用n - 最大匹配数,及最大子集数。
二分匹配中最大独立集=顶点数n-最大匹配
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const MAXN = 40005;
#define INF 0x3f3f3f3f
vector<int>prime;
int p[2][500005];
vector<int> G[MAXN],v[MAXN];
int Mx[MAXN],My[MAXN];
int dx[MAXN],dy[MAXN];
int dis;
bool used[MAXN];//DFS中甬道的访问标记
bool isPrime[MAXN];
LL primeList[MAXN],primeCount = 0;
void primeInit(LL n)
{
memset(isPrime,true,sizeof(isPrime));//初始化认为全部是素数
int m = sqrt(n + 0.5);
for(int i = 2; i <= m; i ++)
{
if(isPrime[i])//判断是素数
{
for(int j = i * i; j <= n; j += i)
{
isPrime[j] = false;
}
}
}
for(int i = 2; i <= n; i ++)
{
if(isPrime[i])
{
primeList[primeCount] = i;
primeCount ++;
}
}
}
vector<int>e[MAXN];
int num[MAXN];
void fun(int k,int n)
{
int m = n;
int number = 0;
for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
{
if(n % primeList[i] == 0)
{
while(n % primeList[i] == 0)
{
n = n / primeList[i];
number ++;
}
e[k].push_back(primeList[i]);
}
}
if(n != 1)
{
e[k].push_back(n);
number ++;
}
num[k] = number;
if(num[k] % 2 == 0)
p[0][m] = k;
else
p[1][m] = k;
}
bool vis[500005];
int a[MAXN];
void unionx(int n)
{
for(int i = 0; i < n; i ++)
{
if(num[i] % 2 == 0)
{
for(int j = 0; j < e[i].size(); j ++)
{
if(p[1][a[i] / e[i][j]] != -1)
{
G[i].push_back(p[1][a[i] / e[i][j]]);
}
}
}
else
{
for(int j = 0; j < e[i].size(); j ++)
{
if(p[0][a[i] / e[i][j]] != -1)
{
G[p[0][a[i] / e[i][j]]].push_back(i);
}
}
}
}
}
int n;
void init(){
for(int i = 0; i <= n; i++){
G[i].clear();
v[i].clear();
}
}
bool SearchP(){
queue<int> q;
dis = INF;
memset(dx,-1,sizeof(dx));
memset(dy,-1,sizeof(dy));
for(int i = 0 ; i < n; i++){
if(Mx[i] == -1){
q.push(i);
dx[i] = 0;
}
}
while(!q.empty()){
int u = q.front();
q.pop();
if(dx[u] > dis)
break;
int l = G[u].size();
for(int i = 0; i < l; i++){
int v = G[u][i];
if(dy[v] == -1){
dy[v] = dx[u] + 1;
if(My[v] == -1)
dis = dy[v];
else{
dx[My[v]] = dy[v] + 1;
q.push(My[v]);
}
}
}
}
return dis != INF;
}
bool dfs(int u){
int l = G[u].size();
for(int i = 0; i < l; i++){
int v = G[u][i];
if(!used[v] && dy[v] == dx[u] + 1){
used[v] = true;
if(My[v] != -1 && dy[v] == dis)
continue;
if(My[v] == -1 || dfs(My[v])){
My[v] = u;
Mx[u] = v;
return true;
}
}
}
return false;
}
int MaxMatch(){
int res = 0;
memset(Mx, -1, sizeof(Mx));
memset(My, -1, sizeof(My));
while(SearchP()){
memset(used, false, sizeof(used));
for(int i = 0; i < n; i++)
if(Mx[i] == -1 && dfs(i))
res++;
}
return res;
}
int main()
{
// freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
primeInit(10000);
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
memset(e,0,sizeof(e));
memset(p,-1,sizeof(p));
memset(vis,false,sizeof(vis));
int ans = 0;
scanf("%d",&n);
for(int i = 0; i < n; i++)
{
scanf("%d",&a[i]);
vis[a[i]] = true;
fun(i,a[i]);
}
unionx(n);
/* for(int i = 0; i < n; i ++)
{
for(int j = 0; j < G[i].size(); j ++)
{
printf("%d ",G[i][j]);
}
printf("\n");
}*/
printf("Case %d: %d\n",t, n - MaxMatch());
for(int i = 0; i < n; i ++)
{
G[i].clear();
}
}
return 0;
}
C.
题意:给你一个矩形的面积,和矩形最小边的长度。已知矩形肯定不是正方形。求有多少种方案。(边长都是整数)
解析:分解质因数。将所有质因数的个数 + 1 求积就是所有的分解的方案数(2倍的),由于不能是正方形,直接将方案数 / 2就是所有的矩形方案。
注意是暴力矩形最小边的长度,如果边长b > sqrt(a) 那肯定是不存在的,这样就将方案数变成了10^6种。只要遍历一遍即可
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 2000006;
bool isPrime[maxn];
LL primeList[maxn],primeCount = 0;
//vector<long long>e;// pay attention to the e which is used to store the prime.may be a huge prime
//直接暴力枚举1到最小值中是n的约数的个数即可
void primeInit(LL n)
{
memset(isPrime,true,sizeof(isPrime));//初始化认为全部是素数
int m = sqrt(n + 0.5);
for(int i = 2; i <= m; i ++)
{
if(isPrime[i])//判断是素数
{
for(int j = i * i; j <= n; j += i)
{
isPrime[j] = false;
}
}
}
for(int i = 2; i <= n; i ++)
{
if(isPrime[i])
{
primeList[primeCount] = i;
primeCount ++;
}
}
}
long long fun(long long n)
{
int number;
long long ans= 1;
for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
{
number = 0;
if(n % primeList[i] == 0)
{
while(n % primeList[i] == 0)
{
n = n / primeList[i];
number ++;
}
ans = ans * (number + 1);
}
}
if(n != 1)
{
ans = ans * 2;
}
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
primeInit(1100000);
long long n,a;
int T;
long long ans= 0;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
printf("Case %d: ",t);
scanf("%lld%lld",&n,&a);
ans = fun(n);
if(a > sqrt(n) || a * a == n)
{
printf("0\n");
continue;
}
else
{
ans = ans / 2;
for(int i = 1; i < a; i ++)
{
if(n % i == 0)
ans --;
}
}
printf("%lld\n",ans);
}
return 0;
}
D.
题意:找从1 到n约数的个数是偶数的个数
解析:奇数 * 偶数 = 偶数,偶数 * 偶数= 偶数,奇数 * 奇数= 奇数。只有当全部的小项都是奇数的时候才是奇数,结果才是奇数。我们统计所有事偶数的个数。
每一项:X = P^(e+ 1) - 1 / ( P - 1) =( P ^ (e + 1) - P) / (P - 1) +1 = 1 + P * (P ^ e - 1) / (P - 1)
(P ^ e - 1) / (P - 1) 等于等比数列求和从1开始到 P ^ (e - 1)的和。
分情况啦,如果P是2的话,他一定就是个奇数,最后这一项就肯定是个奇数。
如果P 不是2的话,如果e是偶数,等比数列这一项就是偶数 + 1,X就是奇数。
反之就是偶数,即不是我们要统计的。
既然除了2这一项,每一个质因子都是一个平方,那么可以直接打表找。但是对于乘以2之后,要保证没有算重了的点。只需暴力一遍质因子的平方 * 2,即可
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int main()
{
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
long long n,ans = 0;
scanf("%lld",&n);
for(long long i = 1; i * i <= n; i ++)
{
ans ++;
if(i * i * 2 <= n)
{
ans ++;
}
}
printf("Case %d: %lld\n",t,n - ans);
}
return 0;
}
E.
题意:求n的k次方的前三位和后三位。
思路:后三位直接快速幂就可以了。前三位:首先任意一个正数都可以表达为p = 10 ^ a.其中a又等于x + y.x是整数部分,y是小数部分。整数部分显然是增加位数的,我们只需考虑小数部分y,(此处用浮点数的模运算)再将小数部分乘以100就行。坑:保证是三个数!!!!
代码:
//pay attention when you output there will be accurate three num
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const mod = 1000;
//x ^m次方,快速幂
long long get_inv(LL x,LL m)
{
LL r,y;
for(r = 1,y = m; y; x = x * x % mod,y >>= 1)
{
if(y % 2 == 1)
r = r * x % mod;
}
return (int) r;
}
int main()
{
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
long long n,k;
scanf("%lld%lld",&n,&k);
int ans = (int) (pow(10.0,fmod(log10(n * 1.0) * k * 1.0,1) ) * 100);
printf("Case %d: %d %03d\n",t,ans,get_inv(n,k));
}
return 0;
}
F.
题意:将一个数分为两个素数相加的方案有几种。要求a <= b
解析:线性的素数打表,然后暴力
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 10000002;
int prime[maxn / 10], pNum;
bool notPrime[maxn];
void Prime(int M)
{
int i, j;
for(i = 2; i < M; i++) {
if(!notPrime[i]) { prime[pNum++] = i; }
for(j = 0; j < pNum && prime[j] * i < M; j++ ) {
notPrime[prime[j]*i] = 1;
if(!(i%prime[j]))
break;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
int T;
Prime(10000000);
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
int n,temp;
int ans = 0;
scanf("%d",&n);
for(int i = 0; i < pNum; i ++)
{
if(prime[i] > n / 2)
break;
temp = n - prime[i];
if(notPrime[temp] == false)
{
ans ++;
}
}
printf("Case %d: %d\n",t,ans);
}
return 0;
}
G.
题意:给以个数n,求i从1到n时 n / i的和。
思路:从1到n的每个数的n/ i设为m。
则n / 1 - n/2 为m == 1出现的次数
n /2 - n / 3 m = 2出现的次数,依次类推
m = i的个数20/i - 20/(i + 1)
为了降低复杂度要前面和后面一起进行计算,即从i = 1,到sqrt(n)同时计算n/ i,和减法 求出的个数。注意如果两个计算的是m出现的词数是相同的,则要去重。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int main()
{
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
long long n,ans = 0;
scanf("%lld",&n);
for(long long i = 1;i * i <=n ; i ++)
{
ans += (n / i - n / (i + 1)) * i + n / i;//n/i - n / (i +1) 表示包括右边边界在内的个数为i的个数
if(n / i == i)
{
ans -= i;
}
}
printf("Case %d: %lld\n",t,ans);
}
return 0;
}
H.
题意:给定n,求i,j其中1<=i <= n, i <= j <= n,求i,j的最小公倍数是n 的个数
解析:首先分解质因数,然后对于每一个质因数,设质因数的指数为e。i,j一共有2*e + 1种情况。这些情况中的i和j都是不一样 的。将所有的(2 * e + 1)相乘,最后除了i和j同时为n的,其他的都出现了两次。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
typedef long long LL;
int const MAXN = 10000100;
bool isPrime[MAXN];
LL primeList[MAXN/10],primeCount = 0;
void primeInit(long long n)
{
memset(isPrime,true,sizeof(isPrime));//先认为都是素数
isPrime[1]=false;
long long m= sqrt(n + 0.5);
for(int i=2;i<=n;i++)
{
if(isPrime[i])primeList[primeCount ++]=i;
for(int j=0;j < primeCount && i*primeList[j]<=n;j++)
{
isPrime[i*primeList[j]]=false;
if(i%primeList[j]==0)break;
}
}
}
int e[MAXN / 10],k = 0;
void fenjie(long long n)
{
int number = 0;
for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
{
if(n % primeList[i] == 0)
{
number = 0;
while(n % primeList[i] == 0)
{
n = n / primeList[i];
number ++;
}
e[k ++] = number;
}
}
if(n != 1)
{
e[k ++] = 1;
}
}
int main()
{
//freopen("text.in","r",stdin);
primeInit(10000111);
// cout<<primeCount - 1<<endl;
int T;
long long n;
scanf("%d",&T);
for(int t =1; t <= T; t ++)
{
memset(e,0,sizeof(e));
k = 0;
scanf("%lld",&n);
fenjie(n);
long long ans =1;
for(int i = 0; i < k; i ++)
{
ans = ans * (e[i] * 2 + 1);
}
ans = ans / 2 + 1;
printf("Case %d: %lld\n",t,ans);
}
return 0;
}
I.
题意:
解析:先将所有的T进行排序,按照n从小到大,这样扫一遍暴力就行了
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
struct PP
{
int id,num;
bool operator < (const PP x)const
{
return num < x.num;
}
} n[10005];
double ans[10005];
int main()
{
// freopen("text.in","r",stdin);
int T;
scanf("%d",&T);
for(int t =1; t <= T; t ++)
{
scanf("%d",&n[t].num);
n[t].id = t;
}
sort(n + 1,n + 1 + T);
double temp = 0,p = 1;
for(int i = 1; i <= T; i ++)
{
for(; p <= n[i].num; p ++)
{
temp += 1.0 / p;
}
ans[n[i].id] = temp;
}
for(int i = 1; i <= T; i ++)
printf("Case %d: %.10lf\n",i,ans[i]);
return 0;
}
J.
题意:x = b ^p 求p的最大值
解析:分解质因数,如果当前质因数的指数能够整除上一个则选小的哪个,反之p为1,另外需要注意如果x是个负数,就需要判断p是否为偶数,如果是偶数的话,就要除以2,直到p变为奇数
代码:
//考虑负数 的情况如果是负数而且答案是偶数,则要把答案变成奇数
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int const MAXN = 100000 + 100;
bool isPrime[MAXN];
LL primeList[MAXN],primeCount = 0;
void primeInit(long long n)
{
memset(isPrime,true,sizeof(isPrime));//先认为都是素数
isPrime[1]=false;
for(int i=2; i<=n; i++)
{
if(isPrime[i])primeList[primeCount ++]=i;
for(int j=0; j < primeCount && i*primeList[j]<=n; j++)
{
isPrime[i*primeList[j]]=false;
if(i%primeList[j]==0)break;
}
}
}
//分解质因数,接口fenjie(n),质因数的个数存在数组e[i]中
int e[MAXN ],k = 0;
void fenjie(long long n)
{
memset(e,0,sizeof(e));
k = 0;
int number = 0;
for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
{
if(n % primeList[i] == 0)
{
number = 0;
while(n % primeList[i] == 0)
{
n = n / primeList[i];
number ++;
}
e[k ++] = number;
}
}
if(n != 1)
{
e[k ++] = 1;
}
}
int main()
{
//freopen("text.in","r",stdin);
primeInit(100000);
int T;
long long n;
scanf("%d",&T);
for(int t = 1; t <= T; t++)
{
bool flag = false;
bool sf = false;
scanf("%lld",&n);
if(n < 0){
sf = true;
fenjie(-n);
}
else
fenjie(n);
int p = -1;
for(int i = 0; i < k; i ++)
{
if(p == -1)
{
p = e[i];
}
else if(p != e[i])
{
if(p < e[i])
{
if(e[i] % p != 0)
{
flag =true;
break;
}
}
else
{
if(p % e[i] != 0)
{
flag = true;
break;
}
else
{
p = e[i];
}
}
}
}
if(flag == true)
{
printf("Case %d: 1\n",t);
}
else if(sf == false)
{
printf("Case %d: %d\n",t,p);
}
else
{
if(p % 2 == 0)
{
while(p % 2 == 0)
p = p / 2;
printf("Case %d: %d\n",t,p);
}
else
printf("Case %d: %d\n",t,p);
}
}
return 0;
}
K.
题意:判断a是否能被b整除。不限正负号
解析:按照白书P314页中的大整数取模来写。就是在将字符串转化为整数的过程中不断的%b,最后判断是否为零即可。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
char a[200];
int main()
{
// freopen("text.in","r",stdin);
int T;
scanf("%d",&T);
int b,ans;
for(int t = 1; t <= T; t ++)
{
scanf("%s%d",a,&b);
ans = 0;
for(int i = 0; i < strlen(a); i ++)
{
if(a[i] == '-')
continue;
ans = (int)(((long long )ans * 10 +a[i] - '0') % b);
}
if(ans == 0)
printf("Case %d: divisible\n",t);
else
{
printf("Case %d: not divisible\n",t);
}
}
return 0;
}
L.
题意:将复杂 度很高的代码
int res = 0;
for( i1 = 0; i1 < n; i1++ ) {
for( i2 = 0; i2 < n; i2++ ) {
for( i3 = 0; i3 < n; i3++ ) {
...
for( iK = 0; iK < n; iK++ ) {
res = ( res + A[i1] + A[i2] + ... + A[iK] ) % MOD;
}
...
}
}
}
转化为复杂度很低的代码。。。。
k层循环,对于每一层循环来说,都是进行了pow(n,k),对于每层循环的每一个A[i]出现的次数都是相同的,即对于每一层来说对结果的贡献都是 : pow(n,k) * sum / n = pow (n,k -1) * sum,对于所有层来说结果就是 pow(n,k - 1) * sum * k,记得mod就可以了
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int cases, caseno = 0;
int n, K, MOD;
int A[1005];
//x ^m次方,快速幂
long long get_inv(LL x,LL m)
{
LL r,y;
for(r = 1,y = m; y; x = x * x % MOD,y >>= 1)
{
if(y % 2 == 1)
r = r * x % MOD;
}
return r;
}
int main()
{
scanf("%d", &cases);
while( cases-- )
{
long long ans = 0;
scanf("%d %d %d", &n, &K, &MOD);
for(int i = 0; i < n; i ++)
{
scanf("%lld",&A[i]);
ans = (A[i] + ans) % MOD;
}
ans = ans * K % MOD;
ans = ans * get_inv(n,K - 1) % MOD;
printf("Case %d: %lld\n", ++caseno, ans);
}
return 0;
}
M.
题意:求闭区间[a,b]中素数的个数。区间较小,a,b很大。
解析:利用类似素数筛选法的方法。先进行素数打表。开数组时直接将数组的第一位当成a。详细见代码
代码:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long LL;
int const MAXN = 10000100;
bool isPrime[MAXN];
LL prime[MAXN/10],primeCount = 0;
void primeInit(LL n)
{
memset(isPrime,true,sizeof(isPrime));//初始化认为全部是素数
int m = sqrt(n + 0.5);
isPrime[1] = false;
for(int i = 2; i <= m; i ++)
{
if(isPrime[i])//判断是素数
{
for(int j = i * i; j <= n; j += i)
{
isPrime[j] = false;
}
}
}
for(int i = 2; i <= n; i ++)
{
if(isPrime[i])
{
prime[primeCount] = i;
primeCount ++;
}
}
} //先用素数筛选法筛选出小范围的素数
bool f[100005];
int qujiansushu(long long a,long long b)
{
int num = 0;
int l = b - a;//将a到b转化为从0到b - a
for(int i = 0 ; i < primeCount && prime[i] * prime[i] <= b ; i++)
{
int j = 0;
if(a % prime[i] != 0)//判断a + j 如果(a + j)% prime[i] != 0,找到离a最近且比a大的位置
j = j + prime[i] - a % prime[i];
if(a + j == prime[i])//如果a + j是素数,则找下一个
j += prime[i];
for(; j <= l ; j += prime[i])
f[j] = 1;//从j开始将含prime[i]因子的数标记(即筛除)
}
for(int i = 0 ; i <= l ; i++)
if(!f[i])
num++;
if(a == 1)//如果a从1开始,需要减去一个
num--;
return num;
}
int main()
{
int t, a, b, p = 0;
scanf("%d", &t);
primeInit(10000000);
while(t--)
{
p++;
memset(f, 0, sizeof(f));
scanf("%d%d", &a, &b);
printf("Case %d: %d\n", p, qujiansushu(a,b));
}
return 0;
}
N
题意:已知n!后面零的个数,求n的最小值。
解析:直接二分搜索。二分的效率是logn 再乘以判断零的个数的时间复杂度。由于t的个数不多。比打表快
代码:
//直接二分搜索求最小的值而不是打表。。。。。
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include <iostream>
using namespace std;
int fun(int x)
{
int ans = 0;
int temp = 5;
while(x >= temp)
{
ans = ans + x / temp;
temp = temp * 5;
}
return ans;
}
int lower_bound(int x,int y,int v)
{
int m;
while(x < y)
{
m = x + (y - x) / 2;
if( fun(m) >= v)
{
y = m;
}
else
x = m + 1;
}
if(fun(x) != v)
return -1;
return x;
}
int main()
{
// freopen("in.txt","r",stdin);
//cout<<k<<endl;
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
printf("Case %d: ",t);
int x;
scanf("%d",&x);
int ans = lower_bound(1,500000015,x) ;
if(ans != -1)
printf("%d\n",ans);
else
{
printf("impossible\n");
}
}
return 0;
}
O.
题意:
已知n,求
代码:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include <iostream>
using namespace std;
int phi[4000005];
void phi_table(int n)
{
for(int i = 2; i <= n; i ++)phi[i] = 0;
phi[1] = 1;
for(int i = 2; i <= n; i ++)
{
if(!phi[i])
{
for(int j = i; j <= n; j += i)
{
if(!phi[j])
{
phi[j] = j;
}
phi[j] = phi[j] / i * (i - 1);
}
}
}
}
long long ans[4000005];
long long f[4000005];
void init()
{
for(int i = 1; i < 4000001; i ++)
{
for(int j = i + i; j < 4000001; j += i )//注意约数不能是它本身
{
f[j] += i * phi[j / i];
}
}
for(int i = 2; i < 4000001; i ++)
{
ans[i] = ans[i - 1] + f[i];
}
}
int main()
{
//freopen("in.txt","r",stdin);
phi_table(4000001);
init();
int n;
while(1)
{
scanf("%d",&n);
if(n == 0)
break;
printf("%lld\n",ans[n]);
}
return 0;
}
P.
题意:给n个除数,每个除数又对应着可以选择的k个余数。输出m个从小到大的同时满足每个除数余数要求的数。
解析:这个题很复杂。首先当选择不同的余数,选择空间较小时将所有的情况都枚举一遍,然后利用中国剩余定理将每种情况得到的数求出来。如果要求输出的总个数比全部的情况得到的值都多,那么则要加上所有除数的成绩。如果枚举的情况非常多,这时,只需寻找n最大,而k最小。即寻找n / k最小的值,然后枚举,并判断是否n个除数都满足。利用set来插入,利用set的.count()来判断该种余数是否是存在的。 values[c].insert(Y[c][i]);values[c].count(n % X[c]);values[c].clear();
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
int const maxc = 15;
int const maxk = 105;
int const LIMIT = 10000;
//计算逆需要用到的扩展 欧几里得算法
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b)
{
d = a;
x = 1;
y = 0;
}
else
{
gcd(b,a % b,d,y,x);
y -= x * (a / b);
}
}
int china (int n, int* s, int* m) {
LL M = 1, d, y, x = 0;
for (int i = 0; i < n; i++)
M *= m[i];
for (int i = 0; i < n; i++) {
LL w = M / m[i];
gcd(m[i], w, d, d, y);
x = (x + y * w * s[i]) % M;
}
return (x+M)%M;
}
int C,X[maxc],k[maxc];
int Y[maxc][maxk];
int a[maxc];
vector<LL> sol;
set <int>values[maxc];
void dfs(int dep)
{
if(dep == C)
{
sol.push_back(china(C,a,X));
}
else
{
for(int i = 0; i < k[dep]; i ++)
{
a[dep] = Y[dep][i];
dfs(dep + 1);
}
}
}
void solve_enum(int s,int bc)
{
for(int c = 0; c < C; c ++)
{
if(c != bc)
{
values[c].clear();
for(int i = 0; i < k[c]; i ++)
{
values[c].insert(Y[c][i]);
}
}
}
for(int t = 0; s != 0; t ++)
{
for(int i = 0; i < k[bc]; i ++)
{
LL n = (LL)X[bc] * t + Y[bc][i];
if(n == 0)
continue;
bool ok = true;
for(int c = 0; c < C; c ++)
{
if(c != bc)
{
if(!values[c].count(n % X[c]))
{
ok = false;
break;
}
}
}
if(ok)
{
printf("%lld\n",n);
if(--s == 0)
break;
}
}
}
}
void solve_china(int s)
{
sol.clear();
dfs(0);
sort(sol.begin(),sol.end());
LL M = 1;
for(int i = 0; i < C; i ++)
{
M *= X[i];
}
for(int i = 0; s != 0; i ++)
{
for(int j = 0; j < sol.size(); j ++)
{
LL n = M * i +sol[j];
if(n > 0)
{
printf("%lld\n",n);
if(--s == 0)
break;
}
}
}
}
int main()
{
//freopen("text.in","r",stdin);
//freopen("out.txt","w",stdout);
int s;
while(scanf("%d%d",&C,&s) == 2 )
{
if(C == 0 && s == 0)
break;
int bestc = 0;
long long tot = 1;
for(int i = 0; i < C; i ++)
{
scanf("%d%d",&X[i],&k[i]);
tot *= k[i];
for(int j = 0; j < k[i]; j ++)
{
scanf("%d",&Y[i][j]);
}
sort(Y[i],Y[i] + k[i]);
if(k[i] * X[bestc] < k[bestc] * X[i])
bestc = i;
}
solve_enum(s,bestc);
printf("\n");
}
return 0;
}
Q.
题意:n * m 大小的网格,其中上下相邻的格子的颜色是不相同的。给定k中颜色,和mod。另外给出一些点,这些点是不能染色的。已知m,k,mod,和mod后一共有几种结果,求n的最小 值。
解析:首先求出如果只有给出点最大的n,求出result看是否成立。这时满足,如果不是最后一行,且它的下面是空的,那么染k种颜色的个数 + 1。如果是第一行被占,那么染k种颜色的个数 -1。再判断只增加一行时候的结果。这时因为可能有在max行上的占用点,有些点染色的种类有k种。之后再增加一行颜色的不同方法肯定是乘以(k- 1)^m次方。将前面的全部设为A,那么就是A* (k - 1) ^m ^add = re(% mod).除以A的逆元, 然后在mod的条件下求log 以(k - 1) ^m次方为敌,re为对数的值
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
LL const mod = 100000007;
LL xa[505],ya[505];
//x ^m次方,快速幂
//x ^m次方,快速幂
long long get_inv(LL x,LL m)
{
LL r,y;
for(r = 1,y = m; y; x = x * x % mod,y >>= 1)
{
if(y % 2 == 1)
r = r * x % mod;
}
return r;
}
//接口:inv(a,n) a : 待求逆的数,n表示mod
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b)
{
d = a;
x = 1;
y = 0;
}
else
{
gcd(b,a% b,d,y,x);
y-= x * (a / b);
}
}
LL inv(LL a,LL n)
{
LL d,x,y;
gcd(a,n,d,x,y);
return d == 1 ? (x + n) % n : -1;
}
LL log_mod (LL a,LL b,LL n)
{
LL m,v,e=1,i;
m=(LL)sqrt(n+0.5);
v=inv(get_inv(a,m),n);
map<int,int>x;
x[1]=0;
for(i=1; i<m; i++)
{
e=(e*a)%n;
if(!x.count(e))x[e]=i;
}
for(i=0; i<m; i++)
{
if(x.count(b))return i*m+x[b];
b=(b*v)%n;
}
return -1;
}
set<pair<LL,LL> >mm;
//注意就没有pow!!!
int main()
{
//freopen("in.txt","r",stdin);
int T;
long long n,k,b,r;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
mm.clear();
long long s = 0;
long long m = 1;
printf("Case %d: ",t);
scanf("%lld%lld%lld%lld",&n,&k,&b,&r);
for(int i = 0; i < b; i ++)
{
scanf("%lld%lld",&xa[i],&ya[i]);
m = max(m,xa[i]);
mm.insert(make_pair(xa[i],ya[i]));
}
long long ans =m;
s += n;
for(int i = 0; i < b; i ++)
{
if(xa[i] != m && !mm.count(make_pair(xa[i] + 1,ya[i])))
{
s ++;
}
if(xa[i] == 1)
{
s --;
}
}
long long A = get_inv(k,s) % mod * get_inv(k - 1,m * n - b - s) % mod;
A = A % mod;
if(A != r)
{
int temp = 0;
for(int i = 0; i < b; i ++)
{
if(xa[i] == m)
{
temp ++;
}
}
A=(A*get_inv(k-1,n-temp))%mod;
A = (A * get_inv(k,temp)) % mod;
A = A % mod;
ans ++;
if(A != r)
{
r = r * inv(A,mod) %mod;
ans = ans + log_mod(get_inv(k - 1,n),r,mod);
}
}
printf("%lld\n",ans );
}
return 0;
}
R.
题意:青蛙A,B沿着赤道都向西跑。青蛙A从x出发,每次跳m米,青蛙B从y出发,每次跳n米。赤道长度为l,问两只青蛙能否跳到一个点上,若能至少跳几次?
解析:列方程x + mt = y + nt (mod l)
直接求解同余模方程。x - y = (n - m) *t + kl,移项得到(m - n)*t - kl = y - x.直接利用扩展欧几里得算法,其中a = m - n,b = k,求解得到x即为t的一个右侧为gcd的解,注意如果右侧(y - x)不能整除gcd则无解,一个解x = 为右侧除gcd * x。然后要求t的最小非负正数解,需要借助一个中间变量p ,p = mod / d.如果p是负数,p = -p.然后只需t = (t %p + p) %d即可
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b)
{
d = a;
x = 1;
y = 0;
}
else
{
gcd(b,a% b,d,y,x);
y-= x * (a / b);
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
LL xx,yy,m,n,l;
while(scanf("%lld%lld%lld%lld%lld",&xx,&yy,&m,&n,&l)!= EOF)
{
LL d,x,y;
LL A = m - n,B = yy - xx;
gcd(A,l,d,x,y);
if(B % d != 0)
{
printf("Impossible\n");
}
else
{
x = x * B / d;
if(d < 0)
d = -d;
l = l / d;
x = (x % l + l )%l ;
printf("%lld\n",x);
}
}
return 0;
}
S.
题意:
for (variable = A; variable != B; variable += C)给A,B,C以及mod2 ^k问进行几次操作后可以到达C
解析:列方程:A + Ct == B % mod
移项:Ct - nmod = B - A
另a = c,b = mod,求解t,注意如果右侧(B- A)不能整除gcd则无解,一个解t = 为右侧除gcd * t再利用中间值p = mod / d,计算最小非负正数解x = (x % p + p) % p
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b)
{
d = a;
x = 1;
y = 0;
}
else
{
gcd(b,a% b,d,y,x);
y-= x * (a / b);
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
LL a,b,c,mod;
while(1)
{
scanf("%lld%lld%lld%lld",&a,&b,&c,&mod);
if(a == 0 && b == 0 && c == 0 && mod == 0)
break;
mod = (LL)1<<mod;
long long d,x,y;
gcd(c,mod,d,x,y);
if((b - a) % d != 0 ||( (a != b) && c == 0))
{
printf("FOREVER\n");
}
else
{
x = x * (b - a) / d;
if(d != 0 &&mod % d == 0)
mod = mod / d;
x = (x % mod + mod )% mod;
printf("%lld\n",x);
}
}
return 0;
}