Codeforces Round #641 (Div. 2)解题报告

A. Orac and Factors:

题目大意:

   k k k次操作,给 n n n每次加上其的最小因数( n n n会改变), k k k次操作之后,输出 n n n

解题思路:

  如果 n n n为奇数,那么其最小因数肯定为奇数,相加之后的 n n n即为偶数,而偶数的最小因子为2,之后 n n n一直为偶数
  因此只要在一开是 f o r for for一遍找出最小因子,加上后,加上 ( k − 1 ) ∗ 2 (k-1)*2 (k1)2即可

AC代码:

#include
using namespace std;
typedef long long ll;
ll t,n,k;
int main() {
	scanf("%lld",&t);
	while(t--) {
		scanf("%lld%lld",&n,&k);
		for(int i=2; i<=n; i++)
			if(n%i==0) {
				n+=i;
				break;
			}
		n+=(k-1)*2;
		printf("%lld\n",n);
	}
} 

B. Orac and Models

题目大意:

  长度为 n n n的序列,问取出最长的子序列满足以下条件,相邻标号,右边的序号是左边的倍数(在原序列中),且值为递增。

解题思路:

  1. 记忆话搜索
  2. dp+预处理所有数的因子(嫌麻烦,于是比赛时没写

AC代码:

#include
using namespace std;
int t,n;
int a[100100],f[100100];//f数组表示以当前位置的数为开头的最长子序列
int dfs(int pos) {
	//system("pause");
	if(f[pos]) return f[pos];//搜过就不搜了
	f[pos]=1;
	for(int i=2; i<=n; i++) {
		if(pos*i>n) break;
		if(a[pos*i]>a[pos]) {//吐槽:一开始在这里,一直写i一直错....
			f[pos]=max(f[pos],dfs(pos*i)+1);
		}
	}
	return f[pos];
}
int main() {
	scanf("%d",&t);
	while(t--) {
		scanf("%d",&n);
		for(int i=1; i<=n; i++)
		    scanf("%d",&a[i]);
		for(int i=1; i<=n; i++)
			if(!f[i]) f[i]=dfs(i);
		int ans=0;
		for(int i=1; i<=n; i++) {
			ans=max(ans,f[i]);
			f[i]=0;
		}
		printf("%d\n",ans);
	}
} 

C. Orac and LCM

题目大意:

   n n n个数,分别为a1,a2…an,构造一个 b b b序列,其中元素为:lcm(a1,a2),lcm(a1,a3)…lcm(a1,an),lcm(a2,an)…,lcm(an-1,an),问这些数的最大公约数是什么?

解题思路:

  最后一秒钟没交上去…第二天就A了…
  第一种方法: g c d ( l c m ( a , b ) , l c m ( a , c ) ) = a ∗ g c d ( b , c ) / g c d ( a , b , c ) gcd(lcm(a,b),lcm(a,c))=a*gcd(b,c)/gcd(a,b,c) gcd(lcm(a,b),lcm(a,c))=agcd(b,c)/gcd(a,b,c)
  什么要证明?对不起,不会ing…留坑
  填坑: g c d ( l c m ( a , b ) , l c m ( a , c ) ) = a ∗ g c d ( b , c ) / g c d ( a , b , c ) gcd(lcm(a,b),lcm(a,c))=a*gcd(b,c)/gcd(a,b,c) gcd(lcm(a,b),lcm(a,c))=agcd(b,c)/gcd(a,b,c) g c d ( l c m ( a , b ) , l c m ( a , c ) ) = l c m ( a , g c d ( b , c ) ) gcd(lcm(a,b),lcm(a,c))=lcm(a,gcd(b,c)) gcd(lcm(a,b),lcm(a,c))=lcm(a,gcd(b,c))等价
  1.将 l c m ( x , y ) lcm(x,y) lcm(x,y)看作x和y质因子的并集, g c d ( x , y ) gcd(x,y) gcd(x,y)看作x和y质因子的交集
  2. 左 式 = ( A ⋃ B ) ⋂ ( A ⋃ C ) 左式=(A \bigcup B) \bigcap (A \bigcup C) =(AB)(AC)
  3. 右 式 = A ⋃ ( B ⋂ C ) 右式=A \bigcup (B \bigcap C) =A(BC)
  4.用点集合知识就能证明 左 式 = 右 式 左式=右式 =
  其余做法:

  1. p k ∣ a n s p^k|ans pkans当且仅当至少有 n − 1 n-1 n1个整数在 a a a数组中满足 p k − 1 ∣ a p^{k-1}|a pk1ai
  2. s o l u t i o n   1 solution ~1 solution 1:找到这样一个 v v v,列举每一个小于等于 p p p的质数,计算最大的 k k ki使pki<=ai,其中排名第二小的ki即满足 p k i ∣ a n s p^{ki}|ans pkians根据1),算出所有的 a n s ans ans的质因数相乘即可
  3. s o l u t i o n   2 solution ~2 solution 2:大致思路:设di为除ai之外的元素,gcd(di)又满足条件1,因此ans=lcm(gcd(d1,d2,…,dn))

AC代码:

#include
using namespace std;
typedef long long ll;
ll n;
ll a[100100], d[100100], b[100100];
ll Gcd(ll a, ll b) {
	if(b == 0) return a;
	else return Gcd(b, a%b);
}
int main() {
	scanf("%lld", & n);
	for(int i = 1; i <= n; i++) scanf("%d", & a[i]);
	d[n] = a[n];//d[i]数组计算a[n]~a[i]的最大公约数
	for(int i = n-1; i >= 1; i--) d[i] = Gcd(d[i+1], a[i]);
	for(int i = 1; i <= n; i++) b[i] = a[i] * d[i+1] / d[i];
	ll ans = b[n];
	for(int i = 1; i <= n-1; i++) ans = Gcd(ans, b[i]);
	cout << ans <<endl;
}

D. Orac and Medians

题目大意:

  一个数组里一段数可以都变成他的中位数,问整个序列中的数能不能都变成 k k k。可以变化很多次。(偷懒ing)

解题思路:

  1. 首先这个序列中要有 k k k
  2. 其次是存在相邻的两个元素大于等于 k k k或者存在间隔一个元素的两个元素大于等于 k k k
  3. ps:这个很好手模,请各位读者手模证明一下,加深印象

AC代码:

#include
using namespace std;
int t, n, k, tag1, tag2;
int a[100100];
int main() {
	scanf("%d", & t);
	while(t--) {
		scanf("%d%d", & n, & k);
		for(int i = 1; i <= n; i++) scanf("%d", & a[i]);
		tag1 = tag2 = 0;//tag1代表是否满足条件1,tag2代表是否满足条件2
		for(int i = 1; i <= n; i++) {
			if(a[i] == k) tag1 = 1;
			if(a[i] >= k && a[i + 1] >= k || a[i - 1] >= k && a[i + 1] >= k) tag2 = 1;
		}
		if(tag1 && tag2 || n == 1 && a[1] == k) printf("YES\n");
		//注意特判n = 1的情况
		else printf("NO\n");
		for(int i = 1; i <= n; i++) a[i] = 0; //初始化
	}
	return 0;
}

你可能感兴趣的:(codeforces)