1. 程式人生 > >Insertion Sort Gym - 101955C 思路+推公式

Insertion Sort Gym - 101955C 思路+推公式

題目:題目連結

題意:對長為n的1到n的數列的前k個數排序後數列的最長上升子序列長度不小於n-1的數列的種數,訓練賽時怎麼都讀不明白這個題意,最後還是賽後問了旁隊才算看懂,英語水平急需拯救55555

思路:明白題意後就很容易了,有一個坑點是k可能比n大,所以我們對k取min(k, n),我們先不考慮前k個數的順序,比k大的數只能有一個出現在前k個數中,而且是要替換k這個位置的數字,並且最後這個數還要不計入最長上升子序列,可以理解為刪掉,如果k + 1存在的話對於這個數要特殊處理,這樣後面的那些數必須保證直接是有序的才可以滿足題目要求,這種情況有n - k種,如果前k個數最大的數才是k,那麼後面的數我們可以選一個不考慮這個數的順序(最後刪掉這個數),組成的種類為1 + (n - k)*(n - k - 1),其中1是每一個數都在排好序的位置上的情況,這裡拿出來防止重複計數,對於我們剛剛提到的k + 1存在的情況下,這時我們可以選一個比k小的數來替換,因為最後可以不刪掉k + 1而是刪掉選定的那個比k小的數。最後,因為前k個數是會排序的,所以我們再乘一個k!就OK啦。

  所以:ans = n - k + 1 + (n - k) * (n - k - 1),如果k + 1存在的話,ans += (k - 1) * (n - k)

AC程式碼(臨近java考試先用java寫一段時間程式碼了5555):

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner in  = new Scanner(System.in);

        long T, n, k, mod;
        T 
= in.nextLong(); for(long t = 1; t <= T; ++t) { n = in.nextLong(); k = in.nextLong(); mod = in.nextLong(); k = Math.min(n, k); long ans = n - k + 1 + (n - k) * (n - k - 1); if(k < n) ans += (k - 1) * (n - k);
for(long i = 1; i <= k; ++i) ans = ans * i % mod; System.out.println("Case #" + t + ": " + ans); } in.close(); } }