BZOJ3193 [JLOI2013]地形生成 【dp】

题目链接

BZOJ3193

题解

注意\(key\)是小于

第一问,显然按高度降序排序,逐个插入
如果高度各不相同,那么之前插入的都比当前插入的\(i\)大,可插入的位置个数就确定了
由于存在高度相同的情况,将key作为第二关键字升序排序
这样后面插入的就一定能插入前面插入的之后,统计一下之前插入了\(cnt\)个相同高度的,可插入的位置就加上\(cnt\)

第二问
如果高度各不相同,答案同第一问
否则要考虑相同高度带来的影响
分段\(dp\)计算每一段相同高度的贡献

我们默认\(key\)大的放在\(key\)小的后面
\(f[i][j]\)为该段第\(i\)个插入位置\(j\)的方案数
首先\(j\)要在可插入范围内
然后就是
\[f[i][j] = \sum\limits_{k = 1}^{j - 1}f[i - 1][k]\]
前缀和优化一下就是\(O(n^2)\)

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define cls(s,v) memset(s,v,sizeof(s))
#define mp(a,b) make_pair<int,int>(a,b)
#define cp pair<int,int>
using namespace std;
const int maxn = 1005,maxm = 100005,INF = 0x3f3f3f3f,P = 2011;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = 0; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 1) + (out << 3) + c - 48; c = getchar();}
    return flag ? out : -out;
}
struct node{int h,key;}e[maxn];
inline bool operator <(const node& a,const node& b){
    return a.h == b.h ? a.key < b.key : a.h > b.h;
}
int n,f[maxn][maxn],sum[maxn];
void work1(){
    int ans = 1,cnt = 0;
    for (int i = 1; i <= n; i++,cnt++){
        if (i == 1 || e[i].h != e[i - 1].h) cnt = 0;
        ans = ans * (min(e[i].key,i - 1 - cnt) + 1 + cnt) % P;
    }
    printf("%d ",ans);
}
void work2(){
    int ans = 1;
    for (int i = 1,nxt; i <= n; i = nxt + 1){
        nxt = i;
        while (nxt < n && e[nxt + 1].h == e[i].h) nxt++;
        int E = min(e[i].key,i - 1) + 1;
        for (int j = 1; j <= E; j++) f[i][j] = 1;
        for (int j = 1; j <= n; j++) sum[j] = (sum[j - 1] + f[i][j]) % P;
        for (int u = i + 1; u <= nxt; u++){
            int cnt = u - i,E = min(e[u].key,u - 1 - cnt) + 1 + cnt;
            for (int j = 1; j <= E; j++){
                f[u][j] = 0;
                f[u][j] = (f[u][j] + sum[j - 1]) % P;
            }
            for (int j = 1; j <= n; j++)
                sum[j] = (sum[j - 1] + f[u][j]) % P;
        }
        int tot = 0; E = min(e[nxt].key,nxt - 1 - nxt + i) + 1 + nxt - i;
        for (int j = 1; j <= E; j++) tot = (tot + f[nxt][j]) % P;
        //printf("mult %d\n",tot);
        ans = ans * tot % P;
    }
    printf("%d\n",ans);
}
int main(){
    n = read();
    REP(i,n) e[i].h = read(),e[i].key = read() - 1;
    sort(e + 1,e + 1 + n);
    work1();
    work2();
    return 0;
}

转载于:https://www.cnblogs.com/Mychael/p/9287624.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值