hdu 2973 YAPTCHA(C++)(威尔逊定理)
hdu 2973 YAPTCHA
题目描述
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Problem Description
The math department has been having problems lately. Due to immense amount of unsolicited automated programs which were crawling across their pages, they decided to put Yet-Another-Public-Turing-Test-to-Tell-Computers-and-Humans-Apart on their webpages. In short, to get access to their scientific papers, one have to prove yourself eligible and worthy, i.e. solve a mathematic riddle.
However, the test turned out difficult for some math PhD students and even for some professors. Therefore, the math department wants to write a helper program which solves this task (it is not irrational, as they are going to make money on selling the program).
The task that is presented to anyone visiting the start page of the math department is as follows: given a natural n, compute
where [x] denotes the largest integer not greater than x.
Input
The first line contains the number of queries . Each query consist of one natural number .
Output
For each n given in the input output the value of Sn.
Sample Input
13
1
2
3
4
5
6
7
8
9
10
100
1000
10000
Sample Output
0
1
1
2
2
2
2
3
3
4
28
207
1609
中文翻译
Problem Description
数学系最近遇到了问题。由于大量未经请求的自动程序在他们的网页上爬行,他们决定在他们的网页上放置Yet-Another-Public-Turing-Test-to-Tell-Computers-and-Humans-Apart(一次公开的图灵测试,以便在他们的网页上区分计算机和人类)。简而言之,要获得他们的科学论文,必须证明自己有资格和有价值,即解决下面一个数学之谜。
然而,对于一些数学博士生甚至一些教授来说,这项测试都很困难。因此,数学系想要编写一个帮助程序来解决这个任务(这不是不合理的,因为他们会在出售程序时赚钱)。
呈现给访问数学部门起始页面的任何人的任务如下:给定一个自然的n,计算
其中[x]表示不大于x的最大整数。(即取整)
Input
第一行包含查询数 。每个查询由一个自然数n组成。
Output
对于输入中给出的每个n,输出Sn的值。
Sample Input
13
1
2
3
4
5
6
7
8
9
10
100
1000
10000
Sample Output
0
1
1
2
2
2
2
3
3
4
28
207
1609
解题思路
题目的意思很简单,就是输入一个n,你按照公式运算输出一个Sn。
先贴出威尔逊定理:当且仅当p为素数时,( p -1 )! ≡ -1 ( mod p )。即:若p为质数,则p能被(p-1)!+1整除。
根据这道题的样子:
很容易就可以联想到威尔逊定理,令p=3k+7,得原式,再讨论p是不是素数即可:
-
当p是素数时,是整数,而不是整数。又题目把取整,所以为1,再把取整还是1。所以在题目的和式中该部分为1;
-
当p是合数时,不是整数,且也不是整数。把取整,所以为不到1的小数,再把取整得到0。所以在题目的和式中该部分为0;
综上,令p=3k+7,(k=1,2,3,…,n),若p为素数,和式为1;若p为合数,和式为0。对k=1~n求和。
所以筛一遍素数,在筛的过程中,找出形如3k+7的素数即可。
筛素数用线筛快,而且上限是3k+7,又k<=n,且,所以要筛的范围是到。用数组valid[i]记录i是否为素数,用数组a[k]记录对应的3k+7=i的k每一和式项是否为1还是0,用s[n]保存结果。
问题解答
#include <iostream>
#include <string.h>
using namespace std;
const int MAXN = 1e6+1;//开a[n]和s[n]的数组大小
const int MAX = 3*(MAXN-1)+7+1;//开判断是否为素数数组的范围
int prime[MAX];//记录哪些数字是素数,即prime[MAX]是一个素数集合
bool valid[MAX];
//数组valid[i]记录i是否为素数。初始所有的valid[i]为true。
//线性筛选完之后valid[i]=ture的就是素数。
void LinearPrime()
{
int tot = 0;
memset(valid,true,sizeof(valid));//初始所有的valid[i]为true
for(int i=2;i<=MAX;++i)//对于2到n中的每一个i
{
if( valid[i] )//如果i是素数
{
prime[tot] = i;//那么就将i这个素数加入素数集合ans[MAX]中
tot++;
}
for(int j=0;j<tot;++j)//对于当前素数集合中每一个元素ans[j]
{
if( i*prime[j]>MAX ) break;//若i*prime[j]>n,结束循环
//(因为确定素数的倍数超过讨论范围,没必要进行下一步了)
valid[i*prime[j]] = false;//将确定素数的倍数记录为合数
if( i%prime[j]==0 ) break;//保证每个合数只会被它的最小质因数筛去
}
}
}
int a[MAXN];//a[i],如果i是素数就为1,不是就为0(初始化全为0)
int s[MAXN];//求和函数,初始化全为0,正好s[1]=0
int main()
{
int t,n;
cin >> t;
LinearPrime();
for(int k=1;k<=MAXN;++k)
{
if(valid[3*k+7]==1)//如果3*k+7是素数
a[k] = 1;//那么对应的a[k]就为1
}
for(int i=2;i<MAXN;++i)
s[i] = s[i-1]+a[i];//s[i]的递推公式
while(t--)
{
cin >> n;
cout << s[n] << endl;
}
}