http://codeforces.com/contest/664/problem/A
给定整数 a,b,求 gcd(a,a + 1,··· ,b)
1 ≤ a ≤ b ≤ 10^100
因为连续的两个自然数是互质的
#include<cstring>
#include<iostream>
using namespace std;
string s,s1;
int main(){
cin>>s;
cin>>s1;
if (s==s1)cout<<s<<endl;else printf("1\n");
}
http://codeforces.com/problemset/problem/235/A
给定整数 n,选出三个不超过 n 的正整数 x,y,z 使得
lcm(x,y,z) 最大
1 ≤ n ≤ 10^6
首先,这三个数应该是互质的.根据 LCM 的公式 (a * b) / gcd(a,b),
gcd(a,b)为1时,即互质,LCM 值是最大的。
然后开始找规律:
如果是奇数的话(n=7),那么结果就是7,6,5这三个数.
因为连续的两个自然数是互质的.相隔一个偶数的两个奇数也是互质的.
所以 n*(n-1)*(n-2) 是结果。如果是偶数的话(n=8,推算一下,应该是8,7,5.把 6 去掉的原因是8和6不是互质。
所以 n*(n-1)*(n-3) 是结果。但是针对于(n=8)再仔细观察一下,8 7 6 5 .
5加3是等于8的.那么万一n跟 n-3 这两个都有因数3的话,n*(n-1)*(n-3)就不可行了
例如 n=12时候,12 11 10 9 .
这时候应该是(n-1)*(n-2)*(n-3).两个奇数夹着一个偶数,能确定是互质的.
那为什么不取12和11,然后从第三个数从8开始找呢?
因为即使找到三个互质的数也没有用,因为11,10,9这三个数都比他们大。
最后,需要考虑因数是4或5或7等等的情况吗?不需要,
因为这些情况组成的 LCM 只会值更小。
#include<cstdio>
long long n;
int main(){
while (~scanf("%I64d",&n)){
if (n<3) printf("%I64d\n",n);else
if (n&1) printf("%I64d\n",n*(n-1)*(n-2));else
if (n%3==0) printf("%I64d\n",(n-1)*(n-2)*(n-3));else
printf("%I64d\n",n*(n-1)*(n-3));
}
}
http://codeforces.com/contest/757/problem/B
题意:给出n个数,选出尽可能多的数,使这些数的gcd不是1.
数论+贪心
选出尽可能多的数,使这些数的gcd不是1.,则它们的gcd是x,x >= 2,
所以可以枚举gcd的值,从2到1e5,
然后枚举倍数 j,x * j (x*j < 1e5)必定在x的集合里所以1<=j<=1e5 ,
即对于每个i 最多可以枚举j = 1e5 / i 个值i * j,
故对于1e5 / x 从1 到1e5积分,得n*lin(n) 约等于 1e6
#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100010
using namespace std;
long long i,n,a[N],x,j,ans,k;
int main() {
scanf("%I64d",&n);
for (i=1; i<=n; i++) {
scanf("%I64d",&x);
a[x]++;
}
ans=1;
for (i=2; i<=N; i++) {
k=0;
for (j=1; j<=N; j++) {
if (i*j<N) {
k+=a[i*j];
} else break;
}
ans=max(ans,k);
}
printf("%I64d\n",ans);
}
http://codeforces.com/problemset/problem/1034/A
首先求出这些数的GCD,然后全部除掉这个GCD,使得题目变成去掉最少的数使得剩余数的GCD不等于1 。
暴力记录一下每个数出现多少次,然后用类似线性筛素数的方法,从2 开始找这些素因子的倍数共出现了几次,然后找出倍数出现次数最多的因子,记录其出现次数t ,答案就是n−t 。
#include<cstdio>
#include<iostream>
#define N 300010
#define INF 15000010
using namespace std;
int b[INF],a[N],i,j,ans,n,g,t;
bool v[INF];
int gcd(int a,int b) {
if (b==0) return a;
else return gcd(b,a%b);
}
int main() {
scanf("%d",&n);
for (i=1; i<=n; i++) {
scanf("%d",&a[i]);
if (i==1) g=a[i];
else g=gcd(g,a[i]);
}
for (i=1; i<=n; i++) {
a[i]=a[i]/g;
b[a[i]]++;
}
for (i=2; i<=INF; i++) {
t=0;
if (!v[i]) {
for (j=i; j<INF; j+=i) {
v[j]=1;
t+=b[j];
}
}
ans=max(ans,t);
}
if (ans==0) printf("-1\n");
else printf("%d\n",n-ans);
}
http://codeforces.com/contest/475/problem/D
枚举右端点,如果gcd不变,则不改变gcd和最左端值的位置。否则更新gcd值和最左端值的位置。
怎样计数呢?由于这样的统计每一轮的若干个不同gcd值把1-i(i为当前右端点的值)
分成了若干段,这一个的最左端的位置就是上一个gcd的最左端的最后一个可取的值的位置+1。
故相当于得到了一个最左端值可取的范围,这样就知道固定右端点能取到的左端点l的
个数即符合的区间个数了。
但要注意左端点和右端点重合的情况(即单独这个数构成的区间)通过这种算法是会漏
掉的,要单独加上去。
#include<cstdio>
#include<map>
#include<iostream>
#define ll long long
#define N 10000010
using namespace std;
map<ll,ll>sum;
map<ll,ll>v;
map<ll,ll>::iterator it,it1;
int n,i,a[N],q,t,x,pre,prev1;
ll gcd(ll a,ll b) {
if (b==0) return a;
else return gcd(b,a%b);
}
int main() {
while (~scanf("%d",&n)) {
sum.clear();
v.clear();
for (i=1; i<=n; i++) {
scanf("%d",&a[i]);
sum[a[i]]++; //左端点和右端点重合的情况单独加
for (it=v.begin(); it!=v.end(); it++) {
x=gcd(a[i],it->first);
if (x!=it->first) {
if (v[x]==0) v[x]=it->second;
it1=it++;
v.erase(it1,it);
it--;
}
}
if (v[a[i]]==0) v[a[i]]=i;
pre=0;
prev1=0;
for (it=v.begin(); it!=v.end(); it++) {
sum[prev1]+=it->second-pre;
pre=it->second;
prev1=it->first;
}
sum[prev1]+=i-pre;
}
scanf("%d",&q);
while (q--) {
scanf("%d",&t);
printf("%I64d\n",sum[t]);
}
}
}