1. 程式人生 > >bzoj4584: [Apio2016]賽艇

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; }