补三道思维题:Codeforces Round #655 (Div. 2)【BCD】

比赛入口


B. Omkar and Last Class of Math

题意:给出一个整数 n n n(1e9范围内),给出条件 a + b = n a+b=n a+b=n,要使得 l c m ( a , b ) lcm(a,b) lcm(a,b)最小,输出 a a a b b b

做法:结论是其中一个答案是 n n n的最大因子(由于不会证明被拖出去枪毙 ),找了找官方题解的证明,总结(翻译 )了一下证明如下:
a = k , b = n − k a=k,b=n-k a=k,b=nk,假设 k ≤ n − k k≤n-k knk,意味着 n − k ≥ n 2 n-k≥ \frac{n}{2} nk2n
如果 k ∣ n k|n kn,即存在 m ∗ k = n , n − k = m ∗ k − k = ( m − 1 ) ∗ k m*k=n,n-k=m*k-k=(m-1)*k mk=n,nk=mkk=(m1)k,这时可得 l c m ( k , n − k ) = n − k < n lcm(k, n-k)=n-klcm(k,nk)=nk<n
如果 k † n k\dagger n kn,且 k † n − k k\dagger n-k knk,那么 l c m ( k , n − k ) ≠ n − k lcm(k,n-k) \ne n-k lcm(k,nk)=nk,且 l c m ( k , n − k ) lcm(k, n-k) lcm(k,nk)会是 k k k n − k n-k nk的乘积,这时 l c m ( k , n − k ) ≥ 2 ∗ ( n − k ) ≥ 2 ∗ n 2 = n lcm(k,n-k)≥2*(n-k)≥2*\frac{n}{2} = n lcm(k,nk)2(nk)22n=n
k ∣ n k|n kn l c m < n lcmlcm<n,而 k † n k\dagger n kn l c m ≥ n lcm≥n lcmn,故 k ∣ n k|n kn l c m lcm lcm存在最小,且当 k ∣ n k|n kn时,因为答案 l c m ( k , n − k ) = n − k lcm(k,n-k)=n-k lcm(k,nk)=nk,要使得 n − k n-k nk尽量小,那么 k k k就得尽量大,故 k k k就是 n n n的最大因子啦。

代码

#include
using namespace std;
typedef long long LL;
LL res = 1e18;
int main() {
  int t; scanf("%d", &t);
  int n;
  while(t--) {
    scanf("%d", &n);
    int a, b, ans = n - 1;
    for(int i = 1; i*i <= n; ++i) {
      if(n % i == 0) {
        ans = min(ans, n-i);
        if(i != 1) ans = min(ans, n-n/i);
      } 
    }
    printf("%d %d\n", ans, n-ans);
  }
  return 0;
} 

C. Omkar and Baseball

题意:有一种操作,会使得一个区间内所有数字位置都和原来位置不同,问最少多少次操作能使得长度为 n n n(2e5范围内)的全排列变为 1 − n 1-n 1n的顺序排列。

做法:一共是三种情况:
首先排除首尾已经在本位上的数字。
1、对于已经排好序的排列结果就是 0 0 0
2、对于完全打乱的排列只需要 1 1 1次操作就能恢复顺序排列。
3、对于部分打乱的排列结果是 2 2 2,第一次让所有位置的都打乱,使得部分打乱的数字能回到原位,第二次还原原本就在本位上的数字,一共两次。

代码

#include
using namespace std;
typedef long long LL;
const int N = 2e5+10;
int a[N];
int main() {
  int t; scanf("%d", &t);
  int n;
  while(t--) {
    deque<int> dq;
    scanf("%d", &n);
    for(int i = 1, x; i <= n; ++i) {
      scanf("%d", &x);
      a[i] = 0; if(x == i) a[i] = 1;
    }
    //排除首位已经排好序的
    int lo = 1, hi = n;
    while(lo <= hi && a[lo]) ++lo;
    while(lo <= hi && a[hi]) --hi;
    if(lo > hi) {
      puts("0"); continue;
    }
    int res = 0;
    for(int i = lo; i <= hi; ++i) {
      if(!a[i]) ++res;
    }
    if(res == hi-lo+1) puts("1");
    else if(res) puts("2");
  }
  return 0;
} 

D. Omkar and Circle

题意:有 n n n(2e5范围内且一定是奇数)个数字组成一个环,可以采用的操作是选取一个数字,用与他相邻的两个数字的和代替他自己,然后删掉相邻的两个数字,重复该操作,直到最后只剩下一个数字,要使得该数字最大,输出该数字。

做法:写出一组数据然后枚举情况就能发现,有固定的间隙的一些数字成为一个组合,这些数字的和是必须加上的,只是间隙所在位置不一样,直接暴力枚举就好啦!

代码

#include 
using namespace std;
typedef long long LL;
const int N=4e5+10;
LL a[N],sum[N];
int main(){
    int n; scanf("%d", &n);
    for(int i = 1; i <= n; i++){
      scanf("%d", &a[i]);
      a[i+n] = a[i];
    }
    sum[1] = a[1];
    for(int i = 2; i <= n*2; i++) sum[i] = sum[i-2] + a[i];
    LL ans = 0;
    for(int i = n+1; i <= n*2; i++) ans = max(ans, sum[i]-sum[i-n-1]);
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(补三道思维题:Codeforces Round #655 (Div. 2)【BCD】)