题目描述
Description
Input
Output
Sample Input
Sample Input1
4 10
3 6 2 9
Sample Input2
8 35
3 7 1 5 10 2 11 6
Sample Output
Sample Output1
6
【样例 1 解释】
共有 6 个排列符合条件,它们是 (1, 3, 2, 4),(2, 4, 1, 3),(3, 1, 2, 4),(3, 1, 4, 2),(4, 2, 1, 3),(4, 2, 3, 1)。
Sample Output2
31384
Data Constraint
题解
原题:LOJ#2743. 「JOI Open 2016」摩天大楼
吼题
显然题目所计算的是一个折线的总长
由于不好从左往右放,所以考虑从下往上放(ai从小到大)
设(f[i][j][k][l=0/1/2])表示放了(a[1sim i]),形成了(j)段,折线在(y=ai)这条线下的长度之和为(k),有(l)段和边界相连
那么当前段的边界个数即为(2j-l),也就是说从(a[i])推到(a[i+1])时(k)会增加((a[i+1]-a[i])*(2j-l))
分类讨论新加的(a[i+1])会放到哪里,那么有五种情况:
①新开一段:(j+1),方案数(j+1-l)
②把某两段相连:(j-1),方案数(j-1)
③和某一段相连:(j)不变,方案数(2j-l)
④和头/尾相连:(j+1,l+1),方案数(2-l)
⑤把头/尾和最靠近的一段相连:(j)不变,(l+1),方案数(2-l)
最后答案为(sum{f[n][1][0sim L][2]})
code
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define add(a,b) a=((a)+(b))%1000000007
#define mod 1000000007
#define file
using namespace std;
int a[1001];
long long f[101][101][1001][3];
int n,L,i,j,k,l,s;
long long ans;
int main()
{
freopen("count.in","r",stdin);
#ifdef file
freopen("count.out","w",stdout);
#endif
scanf("%d%d",&n,&L);
fo(i,1,n)
scanf("%d",&a[i]);
if (n==1)
{
printf("1
");
return 0;
}
sort(a+1,a+n+1);
f[1][1][0][0]=1;
f[1][1][0][1]=2;
fo(i,1,n-1)
{
fo(j,1,n)
{
fo(k,0,L)
{
fo(l,0,2)
if (f[i][j][k][l] && k+(2*j-l)*(a[i+1]-a[i])<=L)
{
s=k+(2*j-l)*(a[i+1]-a[i]);
add(f[i+1][j+1][s][l],f[i][j][k][l]*(j+1-l));
add(f[i+1][j-1][s][l],f[i][j][k][l]*(j-1));
add(f[i+1][j][s][l],f[i][j][k][l]*(2*j-l));
if (l<2)
{
add(f[i+1][j+1][s][l+1],f[i][j][k][l]*(2-l));
add(f[i+1][j][s][l+1],f[i][j][k][l]*(2-l));
}
}
}
}
}
fo(k,0,L)
add(ans,f[n][1][k][2]);
printf("%lld
",ans);
fclose(stdin);
fclose(stdout);
return 0;
}