BZOJ1996 [Hnoi2010]chorus 合唱队

很容易想到区间DP

然后发现这个区间只和圆序列的最后一个数有关,而原序列的最后一个数只可能是现在区间的头或者尾

令$f[i][j][0/1]$表示在区间$[i, j]$之间,原序列的最后一个数是当前区间的头/尾的总答案数

于是只要讨论$a[i], a[i + 1], a[j - 1], a[j]$之间的关系搞一搞就可以了

 1 /**************************************************************
 2     Problem: 1996
 3     User: rausen
 4     Language: C++
 5     Result: Accepted
 6     Time:112 ms
 7     Memory:8700 kb
 8 ****************************************************************/
 9
10 #include <cstdio>
11 #include <cstring>
12
13 using namespace std;
14 const int N = (int) 1e3 + 5;
15 const int mod = 19650827;
16
17 int n, a[N];
18 int f[N][N][2], ans;
19
20 int main() {
21     int i, j, len;
22     scanf("%d", &n);
23     if (n == 1) {
24         puts("1");
25         return 0;
26     }
27     for (i = 1; i <= n; ++i) scanf("%d", a + i);
28     for (i = 1; i < n; ++i)
29         f[i][i + 1][0] = f[i][i + 1][1] = bool(a[i] < a[i + 1]);
30     for (len = 3; len <= n; ++len)
31         for (i = 1; i <= n - len + 1; ++i) {
32             j = i + len - 1, f[i][j][0] = f[i][j][1] = 0;
33             if (a[j] > a[j - 1]) f[i][j][1] += f[i][j - 1][1];
34             if (a[j] > a[i]) f[i][j][1] += f[i][j - 1][0];
35             if (a[i] < a[i + 1]) f[i][j][0] += f[i + 1][j][0];
36             if (a[i] < a[j]) f[i][j][0] += f[i + 1][j][1];
37             f[i][j][0] %= mod, f[i][j][1] %= mod;
38         }
39     ans = (f[1][n][0] + f[1][n][1]) % mod;
40     printf("%d\n", ans);
41     return 0;
42 }

时间: 2024-08-04 10:16:37

BZOJ1996 [Hnoi2010]chorus 合唱队的相关文章

1996: [Hnoi2010]chorus 合唱队

1996: [Hnoi2010]chorus 合唱队 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1057  Solved: 681[Submit][Status][Discuss] Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT Source 题解:萌萌哒DP题,其实和我一开始想的暴力方法很接近,基本上就是记忆化暴力,然后转变为循环形

bzoj1196:[Hnoi2010]chorus 合唱队

这数据范围明显的区间dp啊...然而据说二维会wa...那就写三维把... #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(

BZOJ 1996: [Hnoi2010]chorus 合唱队(dp)

简单的dp题..不能更水了.. --------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i,n) for(int i=0;i<n;++i) #define clr(x,c) memset(x,c,sizeof(x

bzoj 1996: [Hnoi2010]chorus 合唱队

Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT Source 因为只会在区间的两端进行操作,这很容易让我们想到一种区间dp的模型... dp[i][j]表示区间[i,j]已经匹配上了的方案数,因为我们需要知道上一个填的数是什么所以多加一维, dp[i][j][0]表示最后填的数在区间左边,dp[i][j][1]表示最后填的数在区间右边, 这样我们可以确切的知道上一个填的数和当前要填的

[HNOI2010]CHORUS 合唱队

题解: 我们设dp[i][j][0/1]表示最终序列从i到j最后放的是i/j(0/1). 很显然的转移 if(h[i]<h[i+1]) dp[i][j][0]=dp[i+1][j][0]; if(h[i]<h[j]) (dp[i][j][0]+=dp[i+1][j][1])%=mod; if(h[j]>h[j-1]) dp[i][j][1]=dp[i][j-1][1]; if(h[j]>h[i]) (dp[i][j][1]+=dp[i][j-1][0])%=mod; 1 #incl

【BZOJ1996】【Hnoi2010】chorus 合唱队 动态规划

#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43967361"); } 题解: f[N][N][2]暴力维护即可. 代码:(水得我都不敢测样例就直接交了) #include <cstdio> #include <cstring> #include <iost

【BZOJ1996】【HNOI2010】合唱队 [区间DP]

合唱队 Time Limit: 4 Sec  Memory Limit: 64 MB[Submit][Status][Discuss] Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT Main idea 给定一个元素两两不相等的目标序列,每次按照给定方式将一个元素加入到序列当中,问得到目标序列的方案有几种.(加元素的方式:如果加的这个元素比上一个加入的元素小的话则放在队头,否则放在队

[HNOI 2010]chorus 合唱队

Description 题库链接 对于一个包含 \(N\) 个整数的数列 \(A\) ,我们可以把它的所有元素加入一个双头队列 \(B\) . 首先 \(A_1\) 作为队列的唯一元素,然后依次加入 \(A_2\sim A_N\) ,如果 \(A_i<A_{i-1}\) 那么从 \(B\) 的左端加入 \(A_i\) ,否则从 \(B\) 的右端加入 \(A_i\) . 给出最终的队列 \(B\) ,求原数列有多少种可能排列.输出答案对 \(19650827\) 取余. \(1\leq N\le

(DP ) bzoj 1996

1996: [Hnoi2010]chorus 合唱队 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1091  Solved: 702[Submit][Status][Discuss] Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT 区间dp. f[i][j][k]表示区间i到j最后一个放的是i/j(k=0/1)的方案数. 注意f[i][