• P3643 [APIO2016]划艇


    P3643 [APIO2016]划艇

    题意

    一个合法序列可表示为一个长度为 (n) 的序列,其中第 (i) 个数可以为 0 或 ([l_i,r_i]) 中一个整数,且满足所有不为零的数组成的子序列严格上升。求合法序列方案数。

    思路

    朴素动态规划做法为,设 (f_{ij}) 表示第 (i) 个数不为零且数量为 (j) 的方案数,则

    [ans=sum_{i=1}^nsum_{j=l_i}^{r_i}f_{ij}\ f_{ij}=sum_{k=1}^{j-1}sum_{q=0}^{i-1}f_{qk},jin[l_i,r_i] ]

    但是第二维枚举太多。考虑优化。

    首先,我们考虑一段区间,按照上面的方式递推需要依次枚举数量再枚举 (i-1) 个数的方案进行转移,不能够简化的主要原因是因为每个数都有一个限定的区间。若不加限定,发现这段转移可以简化为一个简单问题:每个数可以选取值范围内任意的值或 0,求所有不为零的数组成的子序列严格上升方案数。设取值区间大小为 (len),数的数量为 (n),则答案为 (inom{len+n}n)。可以理解为额外增加 (n) 个 0 表示选 0。

    所以我们进行离散化,将取值范围分为若干段,每个数的范围由若干这样的段组成。对于每一段我们都可以按照上面的方法转移。即对于这一段区间 (j),对于所有包含它的数字,可以从 (0)(i-1) 中任意一种状态 (k) 转移得到,并且需要乘上在 (k)(i) 中选任意个区间包含 (j) 的数字不为 0 的方案数,即 (inom{len+m-1}m) 其中 (m) 为上述数的个数,(len) 为第 (j) 段的长度,减 1 是因为第 (i) 个必选。即

    [ans=sum_{i=1}^nsum_{j=l_i}^{r_i}f_{ij}\ f_{ij}=sum_{q=0}^{i-1}inom{len+m_{jq}-1}{m_{jq}}sum_{k=1}^{j-1}f_{qk},jin[l_i,r_i],m_{jq}=sum_{o=q+1}^i[jin[l_o,r_o]],len为区间j的长度 ]

    发现后面的求和维护一个前缀和即可。总时间复杂度 (O(n^3))

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cctype>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int read(){
    	int w=0,x=0;char c=getchar();
    	while(!isdigit(c))w|=c=='-',c=getchar();
    	while(isdigit(c))x=x*10+(c^48),c=getchar();
    	return w?-x:x;
    }
    namespace star
    {
    	const int maxn=505,mod=1e9+7;
    	int n,m,l[maxn],r[maxn],b[maxn<<1],C[maxn],inv[maxn],f[maxn],ans;
    	inline void work(){
    		n=read();
    		inv[1]=1;for(int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    		for(int i=1;i<=n;i++) l[i]=b[(i<<1)-1]=read(),r[i]=b[i<<1]=read(),b[i<<1]++;
    		sort(b+1,b+1+(n<<1));
    		m=unique(b+1,b+1+(n<<1))-b-1;
    		for(int i=1;i<=n;i++) l[i]=lower_bound(b+1,b+1+m,l[i])-b,r[i]=lower_bound(b+1,b+1+m,r[i]+1)-b;
    		C[0]=f[0]=1;
    		for(int j=1;j<m;j++){
    			int len=b[j+1]-b[j];
    			for(int i=1;i<=n;i++) C[i]=1ll*C[i-1]*(len+i-1)%mod*inv[i]%mod;
    			for(int i=n;i;i--) if(l[i]<=j and r[i]>=j+1){
    				int cnt=1;
    				for(int k=i-1;~k;k--) f[i]=(f[i]+1ll*C[cnt]*f[k])%mod,cnt+=l[k]<=j and r[k]>=j+1;
    			}
    		}
    		for(int i=1;i<=n;i++) ans=(ans+f[i])%mod;
    		printf("%d
    ",ans);
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    
  • 相关阅读:
    CF854C Planning优先队列|set
    暑假集训-8.9总结
    暑假集训-8.6总结
    AcWing 206. 石头游戏 矩阵乘法|矩阵快速幂
    暑假集训-8.5总结
    KMP 和 扩展KMP
    暑假集训-8.4总结
    暑假集训-8.3总结
    2019牛客暑期多校第二场题解FH
    AcWing 196. 质数距离
  • 原文地址:https://www.cnblogs.com/BrotherHood/p/14429628.html
Copyright © 2020-2023  润新知