hihoCoder挑战赛31

Posted PoorLitt1eThin9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hihoCoder挑战赛31相关的知识,希望对你有一定的参考价值。

#1595 : Numbers

时间限制:8000ms
单点时限:1000ms
内存限制:256MB

描述

给定n个整数常数c[1], c[2], ..., c[n]和一个整数k。现在需要给2k个整数变量x[1], x[2], ..., x[k], y[1], y[2], ..., y[k]赋值,满足

(1)对于所有1 ≤ i ≤ k,都有x[i] ≤ y[i]。

(2)对于所有1 ≤ i ≤ n,都存在至少一个j (1 ≤ j ≤ k),使得x[j] ≤ c[i] ≤ y[j]。

求出S=(y[1] + y[2] + ... + y[k]) - (x[1] + x[2] + ... + x[k])的最小值。

输入

第一行两个整数n, k。(1 ≤ n, k ≤ 100000)
接下来n行,每行一个整数c[i]。 (-1000000000 ≤ c[i] ≤ 1000000000)

输出

输出一个整数表示S的最小值。

样例解释

x[1]=-5, y[1]=4,

x[2]=10, y[2]=10.

样例输入
5 2
-5
0
10
4
0
样例输出
9

首先,如果k≥n则可以零距离夹住每个c,此时答案为0。

当k<n时,只需将n个数字从小到大排序,去掉其中最长的k-1个间隔即可。

技术分享
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
long long c[100005], d[100005];
int cmp(const void * x, const void * y) {
    return *((long long *) x) > *((long long *) y);
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &c[i]);
    }
    if (k >= n) {
        printf("0\n");
        return 0;
    }
    qsort(c, n, sizeof(long long), cmp);
    for (int i = 0; i < n - 1; i++) {
        d[i] = c[i + 1] - c[i];
    }
    qsort(d, n - 1, sizeof(long long), cmp);
    long long ans = c[n - 1] - c[0];
    for (int i = 1, j = n - 2; i < k; i++, j--) {
        ans -= d[j];
    }
    printf("%lld\n", ans);
    return 0;
}
View Code

#1596 : Beautiful Sequence

时间限制:11000ms
单点时限:1000ms
内存限制:256MB

描述

对于一个正整数列a[1], ... , a[n] (n ≥ 3),如果对于所有2 ≤ i ≤ n - 1,都有a[i-1] + a[i+1] ≥ 2 × a[i],则称这个数列是美丽的。

现在有一个正整数列b[1], ..., b[n],请计算:将b数列均匀随机打乱之后,得到的数列是美丽的概率P。

你只需要输出(P × (n!))mod 1000000007即可。(显然P × (n!)一定是个整数)

输入

第一行一个整数n。 (3 ≤ n ≤ 60)
接下来n行,每行一个整数b[i]。 (1 ≤ b[i] ≤ 1000000000)

输出

输出(P × (n!))mod 1000000007。

样例输入
4
1
2
1
3
样例输出
8

 满足美丽性质的序列形状为V字形,即先递减后递增,单调序列可以理解为左边或右边长度为0的V字形。

V字形最低点的数字一定是数列中最小的数字,把数列按从小到大的顺序依次添加到两端,只要保证每次添加时和该端最边上的两个数字满足美丽性质,即可实现美丽数列的构造。

首先按从小到大的顺序排序

令dp[i][j][k][l]为当前的数列最左边的两个的下标分别为ij,最右边的两个数字的下标分别为kl时的美丽数列个数。若i=j,表示右边没有了;若k=l,表示左边没有了。

可以通过一个四重循环ijkl实现状态转移,第一重表示当前要添加下标为i的数字,显然,此时数列最左端或最右端的数字中一定有一个编号为i-1,用三重循环jkl枚举另外三个数字。i-1在左边或右边分两种情况,每种情况下各自可能将i添加到左边或右边,一共四次判断,复杂度为O(n^4)。

再想一想的话,其实在循环i时,两端的四个数字里除了必定有一个i-1之外,i-2肯定也在其中,所以其实只用枚举另外两个数字就行了,这种情况下要写八次判断,复杂度为O(n^3),应该也是可以的,不过没有试。

技术分享
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int n;
long long dp[65][65][65][65], a[130];
int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &a[i]);
    }
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (a[i] > a[j]) {
                long long t = a[i];
                a[i] = a[j];
                a[j] = t;
            }
        }
    }
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        if (a[i] == a[0]) {
            cnt++;
        }
    }
    for (int i = 1; i < n; i++) {
        a[i] = a[i + cnt - 1];
    }
    n = n - cnt + 1;
    memset(dp, 0, sizeof(dp));
    dp[0][0][0][0] = dp[0][1][0][1] = dp[1][0][1][0] = 1;
    for (int i = 2; i < n; i++) {
        for (int j = 0; j < i; j++) {
            for (int k = 0; k < i; k++) {
                for (int l = 0; l < i; l++) {
                    //dp[i-1,j,k,l]
                    if (i - 1 != j && k != l) {
                        if ((long long)(a[i] + a[j]) >= (long long)(2 * a[i - 1])) {
                            dp[i][i - 1][k][l] += dp[i - 1][j][k][l];
                            dp[i][i - 1][k][l] %= 1000000007;
                        }
                        if ((long long)(a[k] + a[i]) >= (long long)(2 * a[l])) {
                            dp[i - 1][j][l][i] += dp[i - 1][j][k][l];
                            dp[i - 1][j][l][i] %= 1000000007;
                        }
                    }
                    //dp[j,k,l,i-1]
                    if (j != k && l != i - 1) {
                        if ((long long)(a[i] + a[k]) >= (long long)(2 * a[j])) {
                            dp[i][j][l][i - 1] += dp[j][k][l][i - 1];
                            dp[i][j][l][i - 1] %= 1000000007;
                        }
                        if ((long long)(a[l] + a[i]) >= (long long)(2 * a[i - 1])) {
                            dp[j][k][i - 1][i] += dp[j][k][l][i - 1];
                            dp[j][k][i - 1][i] %= 1000000007;
                        }
                    }
                }
            }
        }
    }
    long long ans = 0;
    if (n > 1) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    ans += dp[n - 1][i][j][k];
                    ans += dp[i][j][k][n - 1];
                    ans %= 1000000007;
                }
            }
        }
    } else {
        ans = 1;
    }
    for (int i = 1; i <= cnt; i++) {
        ans = (ans * i) % 1000000007;
    }
    printf("%lld\n", ans);
    return 0;
}
View Code

 

以上是关于hihoCoder挑战赛31的主要内容,如果未能解决你的问题,请参考以下文章

hihoCoder挑战赛1 毁灭者问题

hihoCoder挑战赛28 题目1 : 异或排序

hihoCoder挑战赛28题目3 : 树的方差

hihoCoder挑战赛28题目2 : 二进制翻转

hihocoder第233周

hihoCoder 1048 : 状态压缩·二