EOJ 2018.1.29新生训练Week2

总体来说不算好做……但是确实也不难。

A

给定正整数n,k,求 f(n,k)=ni=1ik f ( n , k ) = ∑ i = 1 n i k ,结果对19260817取模。
1 <= n <= 1e7, 0 <= k <= 1e9
时限0.698s(???)


上来用拉格朗日插值法直接WA。。正解其实比想象的简单。先欧拉筛出素数,对素数用快速幂求出i^k存起来。然后对合数的i^k只要用素数的结果求就可以了。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int p = 19260817;
const int maxn = 1e7+5;

bool flag[maxn];
ll f[maxn], prime[maxn], n, k, cnt;

inline ll pow_mod(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

inline void sieve()
{
    for (int i = 2; i <= n; ++i)
    {
        if (!flag[i])
        {
            prime[cnt++] = i;
            f[i] = pow_mod(i, k);
        }
        for (int j = 0; j < cnt && i * prime[j] <= n; ++j)
        {
            flag[i * prime[j]] = 1;
            f[i * prime[j]] = f[i] * f[prime[j]] % p;
            if (i % prime[j] == 0) break;
        }
    }
}

int main()
{
    scanf("%lld%lld", &n, &k);
    ll sum = 1;
    sieve();
    for (ll i = 2; i <= n; ++i)
        sum = (sum + f[i]) % p;
    printf("%lld\n", sum);
    return 0;
}

B

给出一个长度为 N 的整数数列 A,对于这个数列进行任意多操作。每次选择一个任意的整数,并将任意 P 个数字加上这个数字。输出 YES 或 NO,表示能否通过这种方法将这个数列中每个数字同时变成零。
1 ≤ P ≤ N ≤ 1e5, |Ai| <= 1e6


结论题。显然当n==p时只有所有数字相等时才是YES;n>p时,数字总和如果是p的倍数则YES。其实凭感觉可以想到:要把所有数字变成0,和必须是p的倍数。不过严格的证明则是将所有n>p的情况转化为n==p+1的情况。

#include <bits/stdc++.h>
using namespace std;

int n, p, i, tmp, sum;

int main()
{
    cin >> n >> p;
    if (n == p)
    {
        scanf("%d", &tmp);
        for (i = 1; i < n; ++i)
        {
            scanf("%d", &sum);
            if (sum != tmp) break;
        }
        printf("%s\n", i == n ? "YES" : "NO");
    }else
    {
        for (i = 0; i < n; ++i)
        {
            scanf("%d", &tmp);
            sum += tmp;
        }
        printf("%s\n", sum % p ? "NO" : "YES");
    }
    return 0;
}

C

给任意一个大于 1 的正整数 N,输出 N 可以分解成最少几个质数(可以相同)的和。
2 ≤ N ≤ 1e15


据说是Codeforces原题。
如果本身是质数那么直接输出1。
运用哥德巴赫猜想,任何大于2的偶数可以被分解为两个质数之和。也就是偶数输出2。
如果n是奇合数但还可以被分解为两个质数,那只可能是一奇一偶。偶质数只能是2,也就是说n-2必须是质数。
其他情况输出3。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll n;

inline bool isprime(ll m)
{
    for (ll i = 2; i * i <= m; ++i)
        if (!(m % i)) return 0;
    return 1;
}

int main()
{
    cin >> n;
    if (n == 2) printf("1\n");
    else if (!(n&1)) printf("2\n");
    else if (isprime(n)) printf("1\n");
    else if (isprime(n-2)) printf("2\n");
    else printf("3\n");
    return 0;
}

D

给出 n 个正整数,问有多少种方法在这 n 个数字的中取其中一些数字,使得这些数字之和超过 k。若答案超过 20 000 000,输出 -1。
1 ≤ n ≤ 1e4
1 ≤ ai ≤ 1e8
1 ≤ k ≤ 1e10


降序排序,预处理前缀和,然后dfs+剪枝。
最优性剪枝:如果当前和已经大于k则剪枝,ans要加上2^(剩余数字个数)——这一步还可以剪枝:由于2^25>2e7,所以一旦剩余数字个数大于等于25也剪枝。
可行性剪枝:如果当前和加上后面所有数(用前缀和)都不大于k那么剪枝。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e4+5;
int a[maxn], n, ans;
bool flag;
ll k, suma[maxn];

inline void dfs(int dep, ll sum)
{
    if (flag || sum + suma[n]-suma[dep-1] <= k) return;
    if (sum > k)
    {
        n-dep+1 >= 25 ? flag = 1 : ans += 1<<(n-dep+1);
        return;
    }
    dfs(dep+1, sum + a[dep]);
    dfs(dep+1, sum);
}

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; ++i)
        scanf("%d", a+i);
    sort(a+1, a+1+n, greater<int>());
    for (int i = 1; i <= n; ++i)
        suma[i] = suma[i-1] + a[i];
    dfs(1, 0);
    printf("%d\n", flag ? -1 : ans);
    return 0;
}

E

给出整数数列 {an},对整个数列进行尽可能少的次数操作,每次操作可以将数列中任意一项加 1 或者减 1,使得最终的数列 b1,b2,b3,…,bn 满足对数列中的任一项 bi (i>=2),有 bi=bi1+i b i = b i − 1 + i
求最少的操作次数。1 ≤ n ≤ 1e5,1 ≤ ai ≤ 1e10


设{cn}={a1,a2-2,a3-5,a4-9…},这题等价于求 |c1x|+|c2x|+...+|cnx| | c 1 − x | + | c 2 − x | + . . . + | c n − x | 的最小值。根据高中函数知识,x应该取cn的中位数。那么我们构造出cn然后排下序取中间下标,这题就做完了……

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e5+5;
ll a[maxn], b[maxn], ans;
int n;

inline ll fun(ll mid)
{
    ll ret = 0;
    for (int i = 1; i <= n; ++i)
        ret += abs(mid - a[i]);
    return ret;
}

int main()
{
    cin >> n;
    b[2] = 2;
    for (int i = 3; i <= n; ++i)
        b[i] = b[i-1] + i;
    for (int i = 1; i <= n; ++i)
    {
        scanf("%lld", a+i);
        a[i] -= b[i];
    }
    sort(a+1, a+1+n);
    printf("%lld\n", fun(a[(n+1)>>1]));
    return 0;
}

F

对于给定的数字串 a1,a2,a3,,an a 1 , a 2 , a 3 , … , a n ,每次可以进行如下操作: 选择一个数 i (1 < i < n),将 ai a i 变成 ai+1+ai1ai a i + 1 + a i − 1 − a i 。问在经过任意多次的操作后,该数列的数字总和最小为多少?
1 ≤ n ≤ 1e5,0 ≤ ai ≤ 1e10


ci=ai+1ai c i = a i + 1 − a i ,这样c数列有n-1项。注意到对 ai a i 的操作等价于交换 ci c i ci+1 c i + 1 ,那么通过将c升序排序后反构造出的a数列就是总和最小的数列啦。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e5+5;
ll a[maxn], c[maxn], sum;

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i)
        scanf("%lld", a+i);
    for (int i = 0; i < n-1; ++i)
        c[i] = a[i+1] - a[i];
    sort(c, c + n-1);
    sum = a[0];
    for (int i = 1; i < n; ++i)
    {
        a[i] = a[i-1] + c[i-1];
        sum += a[i];
    }
    printf("%lld\n", sum);
    return 0;
}

到今天为止,终于把以前写的需要多一些深入思考的题目记录完毕了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值