码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 5542 The Battle of Chibi(2015CCPC - C题)

时间:2015-11-05 18:40:08      阅读:238      评论:0      收藏:0      [点我收藏+]

标签:

  题目链接:hdu 5542

  首届CCPC的C题,比赛时一起搞了好久,最后是队友A出的,当时有试过用树状数组来优化 dp,然后今天下午也用树状数组搞了一下午,结果还是踩了和当时一样的坑:我总是把用来记录状态的 dp 数组和树状数组里的内置数组混在一起使用了,而且两重循环的顺序也反了,以至于两组数据

3 2               3 2

1 2 3     和     3 2 1

程序跑都得出了相同的结果,无语。。。之前做 dp 和线段树结合的题时也是傻傻地分不清,说到底就是 dp 的功力很不够,所以在 dp 专场的这场比赛中除了水题外我几乎没什么贡献了,很心塞郁闷了一段时间

  题目思路:先建立一个经典的 dp 模型:f[k][i] 表示长度为 k,以 b[i] 结尾的上升子序列的数量(即 dp 的状态),所以递推方程为 f[k][i] = sum{ f[k - 1][x], 1 <= b[x] <= b[i]-1 },可以看到 k 只依赖于 k-1,而 i 依赖于 b[i] 前面并比 b[i] 小的(和经典的逆序数原理一样),所以可以用树状数组来快速求出,之所以写成 f[k][i] 而不是 f[i][k] 是为了方便树状数组的操作(其实调过来完全可以的,不过脑子一时间转不过弯来,树状数组一直以来都写得太死了,总是模板大法 -_-||)程序实现中还充斥着各种技巧等。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 #define  lowbit(x)  ((x) & -(x))
 6 typedef long long ll;
 7 const int N = 1003;
 8 const ll mod = 1000000007;
 9 
10 int maxn;
11 ll c[N][N];     // c 数组和 maxn 是树状数组所需的数据结构
12 ll f[N][N];     // f[k][i] 表示长度为 k,以 b[i] 结尾的上升子序列的数量(即 dp 的状态)
13 
14 ll sum(int len, int x) {        // 树状数组的两个函数
15     ll res = 0;
16     while(x) {
17         res += c[len][x];             // c[][] 的 len 对应 f[][] 的 k,x 对应 f[][] 的 b[i]
18         if(res >= mod)   res -= mod;
19         x -= lowbit(x);
20     }
21     return res;
22 }
23 
24 void add(int len, int x, ll v) { 
25     while(x <= maxn) {
26         c[len][x] += v;
27         if(c[len][x] >= mod)   c[len][x] -= mod;
28         x += lowbit(x);
29     }
30 }
31 
32 int b[N], a[N];
33 
34 int main() {
35     int t,n,m,Case = 0;
36     scanf("%d",&t);
37     while(t--) {
38         scanf("%d %d",&n,&m);
39         for(int i = 1; i <= n; ++i) {
40             scanf("%d", b + i);
41             a[i] = b[i];
42         }
43         sort(a + 1, a + n + 1);             // 离散化
44         for(int i = 1; i <= n; ++i)
45             b[i] = lower_bound(a + 1, a + n + 1, b[i]) - a;
46 
47         maxn = n + 1;
48         memset(c, 0, sizeof c);
49         memset(f, 0, sizeof f);             // 记得都要清零
50 
51         ll ans = 0;
52         // 两重循环的顺序不能搞错!
53         // 每次对于每个 b[i],都是已经算好了长度 1~k 的上升子序列的数量,即 f[1~k][i] 的值;
54         // 然后再到 b[i + 1];所以是先枚举 1~n 的离散化后各个元素,再枚举长度(1~min(i,m))
55         for(int i = 1; i <= n; ++i) {
56             int top = min(i,m);
57             for(int k = 1; k <= top; ++k) {
58                 if(k == 1)  f[k][i] = 1;
59                 else {
60                     f[k][i] += sum(k - 1, b[i] - 1);        // f[k][i] = sum{f[k - 1][x], 1 <= b[x] <= b[i]-1 }
61                     if(f[k][i] >= mod)   f[k][i] -= mod;
62                 }
63                 add(k, b[i], f[k][i]);      // 把算好的 f[k][i] 更新进树状数组中去
64             }
65             ans += f[m][i];                 // 这时候 f[m][i] 已经算好了,所以加入到答案中
66             if(ans >= mod)   ans -= mod;
67         }
68         printf("Case #%d: %I64d\n", ++Case, ans);
69     }
70     return 0;
71 }

  dp 数组记录状态,然后更新进相应的数据结构中,和 hdu 4521 小明系列问题——小明序列 有相似之处,必须好好体会下 dp 和数据结构结合的这种思想与处理方式。

hdu 5542 The Battle of Chibi(2015CCPC - C题)

标签:

原文地址:http://www.cnblogs.com/Newdawn/p/4940255.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!