Vjudge Contest0 @2018-08-04

前言

XY六题刚去,吴老师又发题下来了。不过幸好不是愚蠢的OI赛制,个人感觉还算可以。


A Spheres CodeChef - KSPHERES

题目描述

给你一些半球,有 N 上半球和 M 下半球。你可以选择半径相同的一对上下半球组成一个球。一个半径严格小于另一个半径的球可以套在大球中。我们称一个有 D + 1 个球嵌套起来排成的序列为 D 序列。求对于 D { 1 , 2 , . . . , C } ,不同序列的数量。如果两个序列在同样位置上的两个球不同,那么这两个序列不同。如果构成两个球的上半球或下半球不同,那么这两个球不同。答案对 10 9 + 7 取模。

约定

  • 1 N , M 10 5
  • 1 C 10 3
  • 1 R C ,其中 R 表示球的半径

分析

我们可以用一个桶排先求出组成一个半径的球的方案数。我们设对于第 i 种半径的球,组成它的方案数为 w a y [ i ] 。我们令 D P [ i ] [ j ] 表示我们在前 i 种球中,选出一个 j 序列的方案数,则 D P [ i ] [ j ] = D P [ i 1 ] [ j 1 ] w a y [ i ] + D P [ i 1 ] [ j ] ,接下来我们就可以很轻松的求解了。

参考程序

注意:本题用桶排即可,简单不易错。

// vjudge 244023 A
#include <cstdio>
#include <algorithm>
typedef long long LL;
const int MAXN = 100005;
const int MAXC = 1005;
const int MOD = 1000000007;

int N, M, C, bu[MAXN], bl[MAXN], DP[MAXC][MAXC], B[MAXC], totb = 0;

int main() {
    scanf("%d%d%d", &N, &M, &C);
    int i, Ui, Li;
    for (i = 0; i < N; i++) {
        scanf("%d", &Ui);
        bu[Ui]++;
    }
    for (i = 0; i < M; i++) {
        scanf("%d", &Li);
        bl[Li]++;
    }
    int tmp, j;
    for (i = 1; i <= 1000; i++) {
        tmp = (LL)bu[i] * bl[i] % MOD;
        if (tmp) B[++totb] = tmp;
    }
    DP[0][0] = 1;
    for (i = 1; i <= totb || i <= C; ++i)
        for (DP[i][0] = j = 1; j <= i; j++) DP[i][j] = ((LL)DP[i - 1][j - 1] * B[i] % MOD + DP[i - 1][j]) % MOD;
    for (i = 1; i <= C; ++i) printf("%d ", DP[totb][i + 1]);
    putchar('\n');
    return 0;
}

B Sereja and Commands CodeChef - SEACO

题目描述

Sereja有一个长度为 n 的序列 A 1 , A 2 , . . . , A n 。初始时所有 A i = 0
Sereja在纸上写下了 m 个操作,编号为 1 ~ m 。共有两类操作:

  • 1 l r ( 1 l r n ):将下标在 [ l , r ] 内的元素的值加1;
  • 2 l r ( 1 l r m ):执行编号在 [ l , r ] 内的所有操作,保证 r 小于当前操作的编号。

请帮 Sereja 执行所有操作。
最终序列元素对 10 9 + 7 取模。

约定

  • 1 n , m 10 5

分析

乍一看这是一道线段树的题,但是我们并不要求在线查询,所以用差分即可。但是这个2号操作比较繁杂,如果直接模拟复杂度是不可想象的。但既然我们已经对数组用上了差分,为何不对操作也用一下差分呢?我们如果从前向后进行操作,那么当进行到2操作的时候,我们并不方便处理它要操作的操作区间;但若我们从后向前扫描这些操作,把差分的过程逆转过来,我们就可以统计出当前操作要被进行几次了,这样就成功转化了这个问题。

参考程序

// vjudge 244023 B
#include <cstdio>
#include <cstring>
const int MAXN = 100005;
const int MOD = 1000000007;
struct Opt {
    int t, l, r;
} O[MAXN];

int N, M, T, dop[MAXN], da[MAXN];

inline void plus(int & x, int dl) { x += dl; if (x >= MOD) x -= MOD; }
inline void subtrc(int & x, int dl) { x -= dl; if (x < 0) x += MOD; }
void solve();

int main() {
    scanf("%d", &T);
    while (T--) solve();
    return 0;
}

void solve() {
    scanf("%d%d", &N, &M);
//  初始化差分数组,dop是操作的差分数组,da是序列的差分数组
    memset(dop, 0, sizeof(int) * (M + 2));
    memset(da, 0, sizeof(int) * (N + 2));
    int i;
    for (i = 1; i <= M; ++i) scanf("%d%d%d", &O[i].t, &O[i].l, &O[i].r);
    int t = 1;
    for (i = M; i > 0; --i) {
        plus(t, dop[i]);
//      因为我们是倒着来的,所以堆操作的差分也要反过来做,这个很好理解
        if (O[i].t == 2) subtrc(dop[O[i].l - 1], t), plus(dop[O[i].r], t);
        else plus(da[O[i].l], t), subtrc(da[O[i].r + 1], t);
    }
    int Ai = 0;
    for (i = 1; i <= N; i++) {
        plus(Ai, da[i]);
        printf("%d ", Ai);
    }
    putchar('\n');
}

C Blocked websites CodeChef - WSITES01

这篇题解独立出来写。
传送门


D Cooking Schedule CodeChef - SCHEDULE

题目描述

大厨是个名扬四海的大厨,所有人都想吃他做的菜。
如你所知,做菜并不是件简单的事情,大厨每天都得做菜,累到不行。因此,大厨决定给自己放几天假。
大厨为接下来的 N 天定制了一个计划。在第 i 天,如果 A i = 1 ,那么大厨会在那天烹饪一道美味佳肴;如果 A i = 0 ,那么大厨就会休息。
在大厨定下计划之后,他意识到他的计划并不完美:有些连着的日子里,大厨天天都得做菜,还是会很累;同时,也有时候大厨会连着休息好几天,这几天什么都不干,大厨也会觉得无聊。
因此大厨决定对这份计划做一些修改,但他也不想改太多地方,因此他决定最多修改 K 天的安排。大厨会选出最多 K 天,对于选出的每一天 i ,如果 A i = 1 ,则将其改成 0;否则将其改成 1。
请你帮大厨写一个程序以决定修改哪些天的安排。修改之后应当保证,具有相同安排(即 A i 相等)的连续一段的日子天数最少。

约定

  • 1 T 11 , 000
  • 1 N 1 , 000 , 000
  • 输入中每组数据的 N 之和 1 , 000 , 000
  • 0 K 1 , 000 , 000
  • 0 A i 1

分析

我们考虑动态规划,但是显然并不好进行转移。注意到题目要求是求最大值最小,那么我们考虑二分。那我们如何检查答案呢?我们可以预处理出原序列中连续的安排相同的天数,对于一个二分出来的天数 l i m ,我们对于原来序列中的每一块,每个 l i m 个改变那天的安排即可。(若要改变那一段最后一天则改变倒数第二天,并且对于 l i m = 1 时进行特判)这样就解决了这个问题。

参考程序

// vjudge 244023 D
#include <cstdio>
#include <algorithm>
const int MAXN = 1000005;

int N, K, A[MAXN], M;
char input[MAXN];

void solve();
bool check(int lim);
bool check1();  // check1()是对1的情况特殊判断

int main() {
    int T;
    scanf("%d", &T);
    while (T--) solve();
    return 0;
}

void solve() {
    scanf("%d%d%s", &N, &K, input);
    char ch = input[0];
    int i, len = 1, lb = 1, ub = 0;
//  分块
    for (i = 1, M = 0; i < N; i++)
        if (input[i] == ch) ++len;
        else A[M++] = len, ub = std::max(ub, len), ch = input[i], len = 1;
    A[M++] = len, ub = std::max(ub, len);
    if (check1()) { puts("1"); return; }
    int mid;
//  二分,当前二分区间为(lb, ub]
    while (ub - lb > 1) {
        mid = lb + ub >> 1;
        if (check(mid)) ub = mid;
        else lb = mid;
    }
    printf("%d\n", ub);
}

bool check(int lim) {
    int res = 0;
    ++lim;
    for (int i = 0; i < M; ++i) res += A[i] / lim;
    return res <= K;
}

bool check1() {
    int cnt = 0;
    for (int i = 0; i < N; i++) cnt += input[i] ^ '0' ^ (i & 1);
    return cnt <= K || (N - cnt) <= K;
}

E Flooring CodeChef - FLOORI4

题目描述

计算

扫描二维码关注公众号,回复: 2759493 查看本文章
i = 1 n i 4 N i

答案对 M 取模。

分析

首先我们要明白一个事情:

i = 1 n i 4 = 1 30 n ( n + 1 ) ( 2 n + 1 ) ( 3 n 2 + 3 n 1 )

这个结论是很好得出来的。
有了这个结论,我们再来考虑 N i ,它的可能的值是 O ( N ) 的。我们可以分块处理,对于 N i 一样的数一起计算,这样就省去很多冗余。
当然还有取模问题,由于模数不一定是个质数,我们不能乘上30的逆元。那么我们要用到如下结论:
a / b m o d c = a m o d ( b c ) / b

证明如下:
我们设 a / b r ( mod c ) ,则有 a b r ( mod b c ) ,那么 a / b r ( mod c ) 。证毕。

那么我们在求四次方和的时候模数取 30 M 即可,最后返回和再除以30。

参考程序

代码十分短小。

// vjudge 244023 E
#include <cstdio>
typedef long long LL;

LL MOD, M, N;

inline LL sum(LL n) { return n % MOD * (n % MOD + 1) % MOD * (2 * n % MOD + 1) % MOD * (3 * n % MOD * n % MOD + 3 * n % MOD - 1) % MOD / 30; }
inline void plus(LL & x, LL d) { x = (x + d + M) % M; }
void solve();

int main() {
    int T;
    scanf("%d", &T);
    while (T--) solve();
    return 0;
}

void solve() {
    scanf("%lld%lld", &N, &M);
    MOD = M * 30;
    LL i, res = 0;
//  注意外面运算时取模仍是M(一开始因为这个WA了好久),然后可能减法后有负数所以再加上M。
    for (i = 1; i <= N; i = N / (N / i) + 1) plus(res, (N / i) % M * (sum(N / (N / i)) - sum(i - 1) + M) % M);
    printf("%lld\n", res);
}

F Permutation HDU - 4917

这篇题解独立出来写。
传送门


总结

其实这次的题目难度并不很大,但是由于基础仍然不太扎实,经验不够丰富,所以成绩并不理想。基础还需要更加加强!

猜你喜欢

转载自blog.csdn.net/HelloHitler/article/details/81460436