先考虑第一个问题(先假设没有山高度相同)
我们把所有山按高度从大到小排序,设这个集合为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#include #include #include #include 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; }