BZOJ4584 & 洛谷3643 & UOJ204:[APIO2016]劃艇——題解
https://www.lydsy.com/JudgeOnline/problem.php?id=4584
https://www.luogu.org/problemnew/show/P3643
http://uoj.ac/problem/204
在首爾城中,漢江橫貫東西。在漢江的北岸,從西向東星星點點地分布著個劃艇學校,編號依次為到。每個學校都擁有若幹艘劃艇。同一所學校的所有劃艇顏色相同,不同的學校的劃艇顏色互不相同。顏色相同的劃艇被認為是一樣的。每個學校可以選擇派出一些劃艇參加節日的慶典,也可以選擇不派出任何劃艇參加。如果編號為的學校選擇派出劃艇參加慶典,那麽,派出的劃艇數量可以在Ai至Bi之間任意選擇(Ai<=Bi)。值得註意的是,編號為i的學校如果選擇派出劃艇參加慶典,那麽它派出的劃艇數量必須大於任意一所編號小於它的學校派出的劃艇數量。 輸入所有學校的Ai、Bi的值,求出參加慶典的劃艇有多少種可能的情況,必須有至少一艘劃艇參加慶典。兩種情況不同當且僅當有參加慶典的某種顏色的劃艇數量不同
dp神題,方程很難想……
(一瞬間我以為我已經傻到無可救藥了直到我看了題解發現的確很難很神……)
參考1:https://www.luogu.org/blog/bestFy0731/solution-p3643
參考2:https://blog.csdn.net/wxh010910/article/details/54177511
參考3:http://45.76.49.80:8000/f/7bd0386ed1b24b8bb390/
看完這三個人還沒看懂的話那接下來看我的理解吧。
不難想到對a和b離散化,並且令f[i][j]表示前i個學校,第i個學校必須取且第i個學校的劃艇數量在j段區間內。
然後就有一個問題了,這樣的話肯定會有幾個學校的劃艇數量區間重合,這時如何統計個數。
那麽根據參考2的思路,考慮另一個問題:給m個長度為len的區間,選k個區間的方案數個數。
顯然地為C(m,k)*C(len,k)。
對1~m的k求和即為sigma(C(m,m-k)*C(len,k))=C(m+len,m)。
現在我們就能求出當有m個學校在同一個數量段時的方案數了。
根據參考3,我們可以列出以下的式子:
令k+1~i中可以取j段的劃艇數量的學校個數為m,則:
f[i][j]=sigma(f[k][l]*C(m+len[j],m))(k<i,l<j)
但是特殊的,當m=0時,C(m+len[j],m)=len[j]。
完後前綴和優化即可。
#include<map> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<vector> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int p=1e9+7; const int N=505; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==‘-‘;ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int n,m,a[N],b[N],c[N*2]; int s[N],inv[N],C[N]; inline void LSH(){ sort(c+1,c+m+1); m=unique(c+1,c+m+1)-c-1; for(int i=1;i<=n;i++){ a[i]=lower_bound(c+1,c+m+1,a[i])-c; b[i]=lower_bound(c+1,c+m+1,b[i]+1)-c; } } inline void init(){ inv[1]=1; for(int i=2;i<=n;i++)inv[i]=(ll)(p-p/i)*inv[p%i]%p; } int main(){ n=read();init(); for(int i=1;i<=n;i++){ a[i]=read(),b[i]=read(); c[++m]=a[i];c[++m]=b[i]+1; } LSH(); s[0]=1;C[0]=1; for(int i=1;i<m;i++){ int len=c[i+1]-c[i]; for(int j=1;j<=n;j++)C[j]=(ll)C[j-1]*(len+j-1)%p*inv[j]%p; for(int j=n;j>=1;j--){ if(a[j]<=i&&i+1<=b[j]){ int f=0,mul=len,cnt=1; for(int k=j-1;k>=0;k--){ (f+=(ll)s[k]*mul%p)%=p; if(a[k]<=i&&i+1<=b[k])mul=C[++cnt]; } (s[j]+=f)%=p; } } } int ans=0; for(int i=1;i<=n;i++)(ans+=s[i])%=p; printf("%d\n",ans); return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+歡迎訪問我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++
BZOJ4584 & 洛谷3643 & UOJ204:[APIO2016]劃艇——題解