【JLOI2013】地形生成

先考虑第一个问题(先假设没有山高度相同)

我们把所有山按高度从大到小排序,设这个集合为S,设一个空集合为V,

我们把山按顺序放入集合V。

考虑第i座山插进去的时候能放的位置,因为现在前面有i - 1座山比它高,再加上本来的第i个位置,它一共有i个位置可以放。这个山的关键值如果为k,它只能在前min(k,i)个位置里挑。

所以第i个山的放的位置的组合为ci = min(k,i)种。ans = c1 * c2 …… * cn;

但是我们现在有山的高度相同,且关键值不同。两座高度相同的山能放的最靠后的位置,一定是关键值大的那个越靠后。所以我们先按高度从大到小,然后高度相同的关键值从小到大排序出S集合。

设[x,y]这段区间的山高度相同。所以 ci = min(x,k) + i - x。因为一座山在满足关键值的情况下,可以也放在高度相同的山前面。

所以最后 得出 ans1 = c1 * c2 …… * cn;

接下来为第二个问题:
我们每次都把高度相同的一起加入,
我们考虑把这个问题转化成最经典的球盒模型,大致能搞成这个样子:
把n个没有标号的球放进m个盒子里,允许有盒子为空,但是第i个球只能放到前pi个盒子里,我们把pi从小到大排序后,可以强行规定pi小的只能出现在pi大的球之前,这样就变成了无标号的了。

于是就有一个dp,设f[i][j]表示第i个球严格放在第j个盒子里的方案数

f[i][j]=∑k=1~j f[i-1][k] 
因为f[i][j-1]=∑k=1~j-1 f[i-1][k]
所以f[i][j] = f[i - 1][j] + f[i][j - 1] 
我们再用滚动数组,得最终方程
f[i]=f[i]+f[i-1]
[x,y]这一段的方案数为c= f[1] + f[2] +……f[min(a[y].key,x) ]
最后再将每个c连乘就可以啦

*以上部分经过他人借见,感谢!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
struct node{int h,key;}a[1005];
const int mod=2011;
int n,f[1005];
bool cmp(const node &a,const node &b){return a.h>b.h||(a.h==b.h&&a.key<b.key);}
void work1()
{
    int i,j,k;
    sort(a+1,a+n+1,cmp);
    int num=0,ans1=1;
    for(i=1;i<=n;i++)
    {
        if(a[i].h!=a[i-1].h)num=i;//num记录相同高度的一段的开头 
        ans1=(ans1*(min(a[i].key,num)+i-num))%mod;
    }
    printf("%d ",ans1);  
}
void work2()
{
    int i,j,k,pos,sum,ans2=1;
    for(i=1;i<=n;i=pos+1)
    {
        memset(f,0,sizeof(f));f[1]=1;
        pos=i;
        while(pos<=n&&a[i].h==a[pos].h)pos++;
        pos--;//a[i].h~a[pos].h相同 
        for(j=i;j<=pos;j++)
          for(k=2;k<=min(a[j].key,i);k++)
            f[k]=(f[k-1]+f[k])%mod;
        sum=0;
        for(j=1;j<=min(a[pos].key,i);j++)sum=(sum+f[j])%mod;
        ans2=(ans2*sum)%mod;   
    }
    printf("%d",ans2);
}
int main()
{
    int i,j,k;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d%d",&a[i].h,&a[i].key);
    work1();
    work2();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/dsb-y/p/11205867.html