目录
F1. Counting Is Fun (Easy Version):
题目大意:
思路解析:
代码实现:
D2. Counting Is Fun (Hard Version):
思路解析:
代码实现:
F1. Counting Is Fun (Easy Version):
题目大意:
思路解析:
我们可以考虑一下,在题目条件下好数组有什么性质。 假如n=4, 那我们令好数组的四个值为 a b c d。 假如我们第一次的选择 [l,r] == [1,2],那么数组值为 1 1 0 0,或者 k k 0 0.第二次选择[l,r] ==[2,3], 那么数组值为 k k+m m 0, 可以发现如果ai增加了f, 那么 ai-1 或者 ai+1 也一定会增加 f。所以可以发现一个特殊的性质 ai <= ai-1 + ai+1。 那么我们便可以利用这个性质来进行dp了。
dp[a][b] 代表在当前 位置i下,ai==b,ai == a的情况下满足好数组的定义的数组个数有多少。那么i+1转移 dp[b][c] = dp[m][b] (max(b-c, 0) <= m <= m ) 对m求和。 这里m的取值范围用到上诉发现的性质即可。 时间复杂度为 O(N^3)
代码实现:
import java.io.*;
import java.math.BigInteger;
import java.util.*;public class Main {static int inf = (int) 2e7;public static void main(String[] args) throws IOException {int t = f.nextInt();while (t > 0) {solve();t--;}w.flush();w.close();br.close();}public static void solve() {int n = f.nextInt(); int k = f.nextInt(); int mod = f.nextInt();int[][] dp = new int[k+1][k+1];dp[0][0] = 1;for (int i = 1; i <= n+2; i++) {int[][] ndp = new int[k+1][k+1];int[][] pre = new int[k+1][k+1];for (int b = 0; b <= k; b++) {pre[b][0] = dp[0][b];for (int a = 1; a <= k; a++) {pre[b][a] = (pre[b][a-1] + dp[a][b]) % mod;}}for (int b = 0; b < k+1; b++) {for (int c = 0; c < k + 1; c++) {if (b > c) ndp[b][c] = (pre[b][k] - pre[b][b-c-1] + mod) % mod;else ndp[b][c] = pre[b][k];}}dp = ndp;}w.println(dp[0][0]);}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());}}
}
D2. Counting Is Fun (Hard Version):
思路解析:
题目大意类似,只是把n的限制范围 调到了 n <= 3000, 根据简单版本的时间复杂度O(N^3) 是过不了的,考虑怎么进行优化,第一题我们是枚举 b 和 c 通过特殊性质来推出a的可行范围,那我们想是否可以只枚举c,然后通过容斥原理来进行转移的加速。
第一个版本的枚举 :
a1 m c: -> a1 >=m-c ---- 相当于 ai-2等于这些数(0 ---- m-c-1) 是无效的
a2 m-1 c: -> a2 >= m-1-c ---- 相当于 ai-2等于这些数(0 ---- m-c-2) 是无效的
a3 m-2 c: -> a3 >= m-2-c ..... ---- 相当于 ai-2等于这些数(0 ---- m-c-3) 是无效的
那么便可以推出一个结论 :f(i,j) = f(i-1,k) - f(i-2,k) * (K - j - k) 对 k = (1,K)进行求和; 然后发现后面两个数组在 O(N)的情况下就可以使用前缀和预处理出来。
代码实现:
import java.io.*;
import java.math.BigInteger;
import java.util.*;public class Main {static int inf = (int) 2e7;public static void main(String[] args) throws IOException {int t = f.nextInt();while (t > 0) {solve();t--;}w.flush();w.close();br.close();}public static void solve() {int n = f.nextInt(); int k = f.nextInt(); int mod = f.nextInt();long[][] dp = new long[n+2][k+1];dp[0][0] = 1;for (int i = 1; i <= n + 1; i++) {long s = 0;for (int j = 0; j <= k; j++) {s += dp[i-1][j];s %= mod;}for (int j = 0; j <= k; j++) {dp[i][j] = s;}if (i >= 2){long[] g = new long[k+1];long[] h = new long[k+1];for (int j = 0; j <= k; j++) {g[j] = dp[i - 2][j];h[j] = dp[i-2][j] * j;if (j > 0){g[j] += g[j-1];h[j] += h[j-1];g[j] %= mod;h[j] %= mod;}}for (int j = 0; j <= k; j++) {long x = (g[k - j] * (k - j) - h[k - j] + mod) % mod;dp[i][j] = (dp[i][j] - x + mod) % mod;}}}w.println(dp[n+1][0]);}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());}}
}