[bzoj 3193][JLOI2013]地形生成

难忘多少个日子之前,我对着这道题死磕了将近1*(4+4+3)h的情景(差不多有一整个学校日:上午+下午+晚自习)……
都是泪啊……

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN 1005
const int inf = 1<<30;
const double eps = 1e-8;
typedef long long ll;
#define rep(I, S, T) for (int I = S; I <= T; I ++)
#define rst(ARR) memset(ARR, 0, sizeof(ARR))
#define Mx(A, B) ((A)>(B)?(A):(B))
#define Mn(A, B) ((A)<(B)?(A):(B))

//#define DEBUG
struct Mount{
    int h, key;
    bool operator<(const Mount &m) const {
        return h>m.h || (h==m.h && key < m.key);
    }
}mount[MAXN];
const int MOD = 2011;
int n;
int dp[MAXN];
int main()
{
    #ifdef DEBUG
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
    #endif
    scanf("%d", &n);
    rep(i, 1, n) 
        scanf("%d%d", &mount[i].h, &mount[i].key);
    sort(mount+1, mount+1+n);
    ll ans1 = 1, ans2 = 1;int last = 1;
    rep(i, 1, n+1) {
        if (mount[i].h != mount[last].h) {
            rst(dp); dp[0] = 1;
            rep(j, last, i-1) {
                int ub = Mn(last, mount[j].key);
                (ans1 *= ub+j-last)%=MOD;
                for (int k = 1; k<=ub-1; k++) 
                    (dp[k] += dp[k-1])%=MOD;
            }
            int sum = 0;
            for (int k = Mn(last, mount[i-1].key)-1; k>=0; k--)
                (sum += dp[k])%=MOD;
            (ans2 *= sum)%=MOD;
            last = i;
        }
    }
    printf("%lld %lld", ans1, ans2);
    return 0;
}

直接DP?怎么弄,状态上没法记录所有山位置。
(膜题解后)
原来要排序啊 还是从高到低排序
这样一来,比当前插入的山高的一定已经插入了,对于每一个位置,都可以找出来有几座山在前面而且更高
后面的山可能插入已经摆好的山中,不能固定高的山的位置
这时,可以考虑把之前的山往后移动!因为新插入的山对之前的山不会造成影响!
(排序啊排序)
启发:本题难点在于看上去没有明显阶段性。要学得心明眼亮,才能发现DP的关键
要注意:高度相同时按关键值小到大排序 (很容易疏忽的情况)

(看题 10h后)
终于想通了第一问 然而 还有第二问
加入高度全部相同,那么结果是一样的,仍然考虑高度相同的一段
设排序后相同的这一段编号区间是[l+1,r] 把这一段往前i座山(已排好)里面插
有l个空位置,每插入一个产生一个新序列 而且这样不会产生重复(我们把[l+1,r]上已经插入的看成一个空位) 答案按理就是C(l, r-l)
遗憾的是这样会产生重复(打脸)口胡结束

不得已之下,我们题解想到了DP。
f[i][j] 是[l+1,r]区间内前i个数字( a[l+1..l+i] ) 插入[1,j]的方案数 (j<=l),其中要求a[r]放在第j位。任务就是给定了每座山的插入区间,求不同的摆放方案。
递推式简洁而优美: f[i][j]=f[i1][j]+f[i][j1] 其中 ja[l+i].key
(为什么这么优美呢?是不是和组合数递推式有点像啊QAQ)
(不同之处也比较明显:在于同一位置可以摆放多个,而且DP过程中加入了关键字大小对于元素位置的限制)
再解释一下方程的意义吧
插入在第j个位置上,前i-1个数字也可以插在[1,j]上;
如果不插入,那这i个数字就想办法插到[1,j-1]上。因为要保证j位置上有一个i,就要把上一个j移动过来得到一种新的合法方案。
也可以从前缀和优化的角度考虑,原理稍微简单,过程稍微繁琐:
f[i][j]=jk=0f[i1][k]
同理 f[i][j1]=j1k=0f[i1][k]
对上式作差也能得到我们的递推方程。
对答案的贡献: min{l,a[r].key}1i=0f[rl][i]
复杂度 O(n2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值