洛谷P3643 [APIO2016]划艇(组合数学)

题面

传送门

题解

首先区间个数很少,我们考虑把所有区间离散化(这里要把所有的右端点变为(B_i+1)代表的开区间)

(f_{i,j})表示考虑到第(i)个学校且第(i)个学校必选,这个学校选择的数在离散后的第(j)个区间内,方案数是多少

怎么转移呢,我们考虑枚举上一个不在第(j)个区间的学校(k),设([k+1,i])中有(a)个学校是可以选在第(j)个区间的,且第(j)个区间的长度为(b),然后暴力枚举这(m)个数中有(q)个选了,那么转移就是

[f_{i,j}=sum_{k=0}^{i-1}left(sum_{p=0}^{j-1}f_{k,p} ight)sum_{q=1}^{min(a,b)}{a-1choose q-1}{bchoose q} ]

(sum_{p=0}^{j-1}f_{k,p})显然是可以做一个前缀和的

然后对于(sum_{q=1}^{min(a,b)}{a-1choose q-1}{bchoose q}),它等于(sum_{q=1}^{min(a,b)}{a-1choose a-q}{bchoose q}),可以理解为有(a+b-1)个数,要选出(a)个,然后枚举前(a-1)个数里选了多少个和后(b)个里选了多少个,那么上面的柿子就等价于({a+b-1choose a})

于是递推式就可以化为

[f_{i,j}=sum_{k=0}^{i-1}{a+b-1choose a}left(sum_{p=0}^{j-1}f_{k,p} ight) ]

然后没有然后了

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1005,P=1e9+7;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
	return res;
}
int A[N],B[N],D[N],inv[N],len[N],C[N],f[N][N],g[N][N];
int n,m,res;
int main(){
//	freopen("testdata.in","r",stdin);
	n=read();
	fp(i,1,n)A[i]=read(),B[i]=read(),D[++m]=A[i],D[++m]=B[i]+1;
	D[++m]=0,sort(D+1,D+1+m),D[m+1]=D[m]+1,m=unique(D+1,D+m+2)-D-1;
	fp(i,1,m-1)len[i]=D[i+1]-D[i];
	fp(i,1,n){
		A[i]=lower_bound(D+1,D+1+m,A[i])-D,
		B[i]=upper_bound(D+1,D+1+m,B[i])-D-1;
	}
	--m;
	inv[0]=inv[1]=1;fp(i,2,n)inv[i]=mul(P-P/i,inv[P%i]);
	f[0][0]=1;
	fp(i,0,m)g[0][i]=1;
	fp(i,1,n){
		for(R int j=A[i],a,b;j<=B[i];++j){
			a=len[j]-1,b=0,C[i]=1;
			fd(k,i-1,0)
				A[k+1]<=j&&B[k+1]>=j?(C[k]=1ll*(++a)*inv[++b]%P*C[k+1]%P):C[k]=C[k+1];
			fd(k,i-1,0)f[i][j]=add(f[i][j],mul(C[k],g[k][j-1]));
		}
		g[i][0]=f[i][0];
		fp(j,1,m)g[i][j]=add(f[i][j],g[i][j-1]);
		res=add(res,g[i][m]);
	}
	printf("%d
",res);
	return 0;
}
原文地址:https://www.cnblogs.com/bztMinamoto/p/10560572.html