bzoj4584: [Apio2016]赛艇

真的是无尽接近单刷这题啊。。。。或许这就是差距吧,把大概的思路方向想出来具体就不会了

心态爆炸

 

首先可以一眼的是这个权值肯定要离散化,然后把每个选择的区间断成一段段

假如枚举到当前学校当前区间,前一个学校比这个区间小的傻子都会转移,主要是处理同区间的转移

我们可以先列一个方程的雏形:f[i][j]表示第i个学校的第j个区间 f[i][j]= ∑∑f[i'][j'](i'<i,j'<j) + cost

可以发现cost是和区间长L和当前联续了几个区间k有关的:

假如现在只连续了两个区间,那么手推一下就可以发现第二个区间的第1个数没有匹配的,第二个区间的第2个数有1个匹配的……

一般的,有s1,all=1,si,j=∑si-1,j'(j'<j)这是一个不断算前缀和的过程,cost=sk,L*D 其中D是一个常数

假如打表或者是手推一下,si,j=si-1,j-1+si,j-1 这个长得有点像组合数的递推式,然后其实它就是一个竖下来的杨辉三角形

其实这个东西的现实意义就是n个学校选1~L的数,选出单调上升的方案数,那么其实相当于在L个数选n个排成一列

对于连续了k个区间转移到k+1,那么其实是D*si,L=>D*si+1,L,相当于乘一个(L-k+1)/k,就可以了

那么f[i][j][k]=∑∑f[i'][j'][k-1]*(L-k+1)/k

特殊处理f[i][j][1]=∑∑∑f[i'][j'][k],这个要拿个数组搞

这样加加优化就可以A了,然而其实我们可以不管这个k

f[i][j]=∑∑f[i'][j']*cost(p)

i倒着枚举,其中p表示枚举到当前有多少个学校涵盖当前区间,cost(p)=∑C(L,p-t)*C(p-1,p-t-1),意思是先选出p-t个出来,第p个学校必须选,其他可选可不选,再把它分配下去

然后这个cost也等于C(L+p-1,p)假如选到了加进去的p-1个,相当于对应的学校不选,选出p个

我们再让j一维取前缀和,就可以省一个for,那么就完成了

把i和j的枚举顺序反过来,先得到区间长外面预处理组合数,用类似背包的做法数组还可以省掉一维j

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

LL inv[1100];
void init(){inv[1]=1;for(int i=2;i<=1000;i++)inv[i]=inv[mod%i]*(mod-mod/i)%mod;}
LL updC(LL c,LL n,LL m)//C(n,m)=>C(n+1,m+1) 
{
    return (c+c*(n-m)%mod*inv[m+1]%mod)%mod;
}

int le[510],re[510],lslen,ls[1100];
LL f[1100],c[1100];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n;init();
    n=read();lslen=0;
    for(int i=1;i<=n;i++)
    {
        le[i]=read(),re[i]=read(),re[i]++;
        ls[++lslen]=le[i],ls[++lslen]=re[i];
    }
    sort(ls+1,ls+lslen+1);
    lslen=unique(ls+1,ls+lslen+1)-ls-1;
    for(int i=1;i<=n;i++)
        le[i]=lower_bound(ls+1,ls+lslen+1,le[i])-ls,
        re[i]=lower_bound(ls+1,ls+lslen+1,re[i])-ls;
    
    memset(f,0,sizeof(f));
    for(int j=1;j<=lslen;j++)f[0]=1;
    for(int j=1;j<=lslen;j++)
    {
        LL L=ls[j]-ls[j-1];
        c[1]=L;for(int i=1;i<=n;i++)c[i+1]=updC(c[i],L+i-1,i);
        for(int i=n;i>=1;i--)
        {
            if(le[i]+1<=j&&j<=re[i])
            {
                int p=1;
                for(int k=i-1;k>=0;k--)
                {
                    f[i]=(f[i]+f[k]*c[p])%mod;
                    if(le[k]<j&&j<=re[k])p++;
                }
            }
        }
    }
    LL ans=0;
    for(int i=1;i<=n;i++)ans=(ans+f[i])%mod;
    printf("%lld\n",ans);
    
    return 0;
}

 

posted @ 2019-01-03 21:05  AKCqhzdy  阅读(182)  评论(0编辑  收藏  举报