Codeforces

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]);
		}
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值