Codeforces Round 936 (Div. 2) ---- E. Girl Permutation ---- 题解 (数论)

E. Girl Permutation:

题目大意:

思路解析:

先理解什么是前缀最大值,他应该满足什么条件,根据定义可知对于 i 如果满足 所以 j < i,并且有 ai > aj,那么ai就是前缀最大值, 换言之如果数组 [a1, a2,a3,....,ai] ai是其中的最大值,那么ai就是前缀最大值。

对于后缀最大值有相反的定义, 如果存在数组 [ai,ai+1,ai+2,......,an] ai是其中的最大值,那么ai就是后缀最大值。

那么首先可以确定的是 a1一定是前缀最大值,an一定是后缀最大值。

题目又给出一个是前缀最大值的下标序列和是后缀最大值的下标序列。p数组和s数组,根据上诉分析,p0应该一定是1,sm2应该一定是n。并且pm1 一定等于 s0, 假设pm1 不等于 s0,根据上诉分析我们可以知道,全局最大值一定是前缀最大值和后缀最大值, 假设他的下标为5,那么根据分析 5之后的下标不可能再出现前缀最大值了,5之前的下标不可能出现后缀最大值,因为没有数能比全局最大值还大,那么就可以得出 pm1 一定等于 s0

那我们可以一开始就确定s0的值就为全局最大值,那么s0之前能选的数 就有_{n-1}^{s0-1}\textrm{}的情况,然后就可以这样将整个数组剩下的数分为两半,左右两边互不影响。那么有了这些数假设有k个,现在 1....pi-1....pi没有确定  那么先让pi拿到这k个数的最大值,pi-1拿到这k个数的次大值,然后 pi-1  + 1-- pi - 1在这k-2个数中任意选,然后这些数并且能按照任意顺序进行排列组合。右边类似,那么这样就统计出了每种情况。

代码实现:

import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
    static int inf = (int) 2e7;
    static int mod = (int) 1e9 + 7;
    static int maxN = (int) 2e5;
    static long[] fac = new long[maxN+5];
    static long[] inv = new long[maxN+5];



    public static void main(String[] args) throws IOException {
        fac[0] = 1;
        for (int i = 1; i <= maxN; i++) {
            fac[i] = fac[i-1] * i % mod;
        }
        inv[0] = 1;
        for (int i = 1; i <= maxN; i++) {
            inv[i] = qkm(fac[i]);
        }
        int t = f.nextInt();
        while (t > 0) {
            solve();
            t--;
        }

        w.flush();
        w.close();
        br.close();
    }

    public static void solve() {
        int n = f.nextInt(); int A = f.nextInt(); int B = f.nextInt();
        int[] a = new int[A]; int[] b = new int[B];
        for (int i = 0; i < A; i++) {
            a[i] = f.nextInt();
        }
        for (int i = 0; i < B; i++) {
            b[i] = f.nextInt();
        }
        if (a[0] != 1 || b[B-1] != n || a[A-1] != b[0]) {w.println(0); return;}
        else {
            long ans = cob(n-1, b[0] -1);
            for (int i = A - 2; i >= 0; i--) {
                ans = ans * cob(a[i+1] - 2, a[i+1] - a[i] - 1) % mod * fac[a[i+1] - a[i] - 1] % mod;
            }
            for (int i = 1; i < B; i++) {
                ans = ans * cob(n - b[i-1] - 1, b[i] - b[i-1] - 1) % mod * fac[b[i] - b[i-1] - 1] % mod;
            }
            w.println(ans);
        }
    }

    public static long cob(int n, int k){
        return fac[n] * inv[k] % mod * inv[n - k] % mod;
    }

    public static long qkm(long a){
        long b = mod - 2;
        long res = 1;
        while (b > 0){
            if ((b & 1) == 1) res = res * a % mod;
            b >>= 1;
            a = a * a % mod;
        }
        return res;
    }

    static PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out));
    static Input f = new Input(System.in);
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public String nextLine() {
            String str = null;
            try {
                str = reader.readLine();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return str;
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public Double nextDouble() {
            return Double.parseDouble(next());
        }

        public BigInteger nextBigInteger() {
            return new BigInteger(next());
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值