2020杭电多校第10场题解

D - Permutation Counting

Time limit: 6000 ms
Memory limit: 262144 kB
Judge:
VJudge
HDUOJ

Problem Description

For a given permutation a 1 , a 2 , ⋯ , a n a_1,a_2,⋯,a_n a1,a2,,an of length n n n, we defined the neighbor sequence b b b of a a a, the length of which is n − 1 n−1 n1, as following:

b i = { 0 a i < a i + 1 1 a i > a i + 1 b_i = \left\{ \begin{array}{ll} 0 & \textrm{\(a_{i} < a_{i+1}\)}\\ 1 & \textrm{\(a_{i} > a_{i+1}\)} \end{array} \right. bi={01ai<ai+1ai>ai+1
.

For example, the neighbor sequence of permutation 1 , 2 , 3 , 6 , 4 , 5 1,2,3,6,4,5 1,2,3,6,4,5 is 0 , 0 , 0 , 1 , 0 0,0,0,1,0 0,0,0,1,0.

Now we give you an integer n n n and a sequence b 1 , b 2 , ⋯ , b n − 1 b_1,b_2,⋯,b_{n−1} b1,b2,,bn1 of length n − 1 n−1 n1, you should calculate the number of permutations of length n n n whose neighbor sequence equals to b b b.

To avoid calculation of big number, you should output the answer module 1 0 9 + 7 10^9+7 109+7.

Input

The first line contains one positive integer T ( 1 ≤ T ≤ 50 ) T (1≤T≤50) T(1T50), denoting the number of test cases. For each test case:

The first line of the input contains one integer n n n, ( 2 ≤ n ≤ 5000 ) (2≤n≤5000) (2n5000).

The second line of the input contains n − 1 n−1 n1 integer: b 1 , b 2 , ⋯ , b n − 1 b_1,b_2,⋯,b_{n−1} b1,b2,,bn1

There are no more than 20 20 20 cases with n > 300 n>300 n>300.

Output

For each test case:

Output one integer indicating the answer module 1 0 9 + 7 10^9+7 109+7.

Sample Input

2
3
1 0
5
1 0 0 1

Sample Output

2
11

题解

题意:给你一个长度为 n − 1 n-1 n1 的序列,其中 1 1 1 代表 > > > 0 0 0 代表 < < <。求出满足此序列的 1 − n 1-n 1n 的排列的个数。

先放出官方图解:

向左向右排列这点我是想到了的,不过没有想到可以用DP去解。

模拟一下递推的过程:

我们把图中的5 4 1 2 3数组称为分析数组。

定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 为枚举到第 i i i 个数字时,第 i i i 个数字在分析数组中的 j j j 位置时的情况数。

  1. 那么初始时,分析数组中只有一个 1 1 1,且情况数只有 1 种:
dp数组
本层:1

分析数组:只有 1

  1. 第一个符号是 > > > ,那么 r < l r<l r<l,把 2 2 2 放到 1 1 1 的左边:
dp数组
上层: 1
本层:1 0

分析数组:只有 2 1 一种。

  1. 第二个符号是 < < < ,那么 l > r l > r l>r,把 3 3 3 放在 2 2 2 的右边:
dp数组
上层: 1 0
本层:0 1 1

分析数组:2 3 12 1 3两种。
3 3 3 在中间的情况数是 1,在右边的情况数是1。

  1. 第三个符号是 < < < ,那么 l > r l > r l>r,把 4 4 4 放在 3 3 3 的右边。
dp数组
上层: 0 1 1
本层:0 0 1 2

分析数组:2 3 4 12 3 1 42 1 3 4 三种。
4 4 4 在第3个位置的情况数是1,在最右边的情况数是2.

其实本层的dp数组就是对上层在不同方向(取决于那一层的符号)上的前缀和。

为了方便写代码,我们把dp三角形变换一下形状:

    1
   1 0
  0 1 1
 0 0 1 2
3 3 3 2 0

变成:

1
1 0
0 1 1
0 0 1 2
3 3 3 2 0

最后对第 n n n 层的 d p dp dp 数组求和就是总情况数。

总之,在分析数组中当我们枚举到第 i i i 个数时, i i i 是放在 i − 1 i-1 i1 的左边还是右边只取决于第 i − 1 i-1 i1 个符号。那么我们只需要关心第 i − 1 i-1 i1 个数的位置就能求出 i i i 可能在分析数组中出现的位置的情况数。我们保存 i i i 所有可能出现的位置对应的情况数以及第 i i i 层的分析数组长度(长度就是 i i i ),这样我们求解 i + 1 i+1 i+1 层的时候就可以只关注第 i i i 层的状态,而无需关心前几层的状态。之后就可以 d p dp dp 求解啦~顺便还可以滚动优化一下空间。

代码

#include <bits/stdc++.h>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 5005;
const int mod = 1000000007;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
LL dp[2][maxn];

void sol() {
    _rep(j, 0, n) dp[0][j] = 0;
    dp[0][0] = 1;
    for(int i = 1, f = 0; i < n; ++i, f ^= 1) {
        _rep(j, 0, i) dp[f ^ 1][j] = 0;
        if(read()) for(int j = i - 1; j >= 0; --j) dp[f ^ 1][j] = (dp[f ^ 1][j + 1] + dp[f][j]) % mod;
        else for(int j = 1; j <= i; ++j) dp[f ^ 1][j] = (dp[f ^ 1][j - 1] + dp[f][j - 1]) % mod;
    }
    LL ans = 0;
    _rep(i, 0, n - 1) ans += dp[(n & 1) ^ 1][i], ans %= mod;
    printf("%lld\n", ans);
}

int main() {
    int T = read();
    _for(i, T) {
        n = read();
        sol();
    }
    return 0;
}

K - Task Scheduler

Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
Judge:
VJudge
HDUOJ

Problem Description

ByteCompany has a server cluster with m m m workers, k k k of which are somehow disconnected.

The task scheduler have just received n n n tasks, and the i i i-th task needs to be executed on t i t_i ti workers.

For an executive order of p 1 , p 2 , … p n p_1,p_2,…p_n p1,p2,pn, the task scheduler will assign workers for them respectively. Specifically, for the i i i-th task p i p_i pi, the scheduler will select t p i t_{p_i} tpi workers randomly from all workers which hasn’t been assigned tasks at this moment. Each of those free workers share a universal equal probability to be selected. Note that disconnected workers may also be selected. In this scenario, the current scheduling will be considered as a failure and retry automatically and immediately. Only when the scheduling of the current task is successful, the next task will be proceeded.

Now you need to find an optimal executive order of p 1 , p 2 , … p n p_1,p_2,…p_n p1,p2,pn to minimize the expected amount of the scheduling process. We guarantee that the amount of connected workers is enough to finish all scheduling.

Input

The input contains several test cases, and the first line contains a single integer T ( 1 ≤ T ≤ 100 ) T (1≤T≤100) T(1T100), the number of test cases.

For each test case, the first line contains three integers n ( 1 ≤ n ≤ 100 ) n (1≤n≤100) n(1n100), m ( n ≤ m ≤ 10000 ) m (n≤m≤10000) m(nm10000) and k ( 0 ≤ k ≤ m − n ) k (0≤k≤m−n) k(0kmn): the number of tasks, the total number of workers and the number of disconnected workers.

The following line contains n n n positive integers t 1 , t 2 , t 3 , … , t n ( n ≤ ∑ i = 1 n t i ≤ m − k ) t_1,t_2,t_3,…,t_n (n≤∑^n_{i=1}t_i≤m−k) t1,t2,t3,,tn(ni=1ntimk), describing the number of needed worker for each task.

Output

For each test case, output n integers p 1 , p 2 , … , p n p_1,p_2,…,p_n p1,p2,,pn on a single line representing the order of tasks to be scheduled.

If there are multiple solutions, output the lexicographically smallest one.

Sample Input

2
2 4 1
1 2
3 3 0
1 1 1

Sample Output

2 1
1 2 3

题解

题意:有 n n n 个任务, m m m 个工人,其中第 i i i 个任务需要随机从没有任务的工人中选择 p i p_i pi 个。但是 m m m 个工人中有 k k k 个是“失联”的,当选到“失联”的工人,你将会再次随机选择未被安排的任务的数量个工人。

为了使安排的轮数的期望值尽可能小,请输出任务的执行顺序。当期望值一样时,输出字典序最小的那个。

很有意思的一道结论题。

k k k 0 0 0 时,所有工人都是有效的工人,那么无论怎么选,轮数一定都是任务的数量。

k k k 不为 0 0 0 时,先选需要工人多的任务。

瞎猜的结论,不会证明[滑稽]

官方题解:

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0; i < (a); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define sc(x) scanf("%d", &x)
using namespace std;
#define maxn 100005
#define LL long long
struct poi{
    int val, i;
    poi(){}
    poi(int val, int i): val(val), i(i) {}
    bool operator < (poi b) {
        if(val != b.val) return val > b.val;
        else return i < b.i;
    }
} a[maxn];
int main() {
    int T;
    sc(T);
    _for(ttt, T) {
        int n, m, k;
        sc(n), sc(m), sc(k);
        _rep(i, 1, n) {
            sc(a[i].val);
            a[i].i = i;
        }
        if(k) sort(a + 1, a + n + 1);
        for(int i = 1; i <= n; ++i) printf("%d%s", a[i].i, (i == n ? "\n":" "));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值