6833. 2020.10.24【NOIP提高A組】T3.justice
阿新 • • 發佈:2020-10-25
有\(n\)個\(x\)和\(m\)個\(y\),每次選\(k\)個數,刪掉它們並加入它們的平均數。
問最後形成的數不同的方案數有多少個。
\(n,m,k\le 3000\)
如果\(x=y\)顯然;如果\(x\neq y\),結果和\(x,y\)的具體取值沒有關係。
證明:操作的過程可以用一棵樹來表示。設\(x_i,y_i\)分別表示深度。那麼最終的取值為\(x\sum k^{-x_i}+y\sum k^{-y_i}\)
假設我們得到了兩組不同的\((\{x_i\},\{y_i\})\),然後列個方程。
兩邊同時除以\(x\),現在得到了一個與\(\frac{y}{x}\)有關的一元一次方程。
整理成\(ax=b\)的形式。
如果\(x\)有多個解,當且僅當\(a=b=0\)。顯然不可能成立(不然可證兩組\((\{x_i\},\{y_i\})\)相等)。
接著發現這個問題中\(x=1\)一定是解。
欽定\(x=0,y=1\)。最終權值為\(\sum k^{-y_i}\)。如果令\(x=y=1\)建一棵同構的樹,那麼有\(\sum k^{-x_i}+\sum k^{-y_i}=1\)。這啟示我們:如果一個狀態合法,當且僅當存在\(z\)可以如此表示:\(z=\sum k^{-y_i},1-z=\sum k^{-x_i}\)
假如\(z=\sum_{i>0} c_ik^{-i}\)。如果不進位,\(\sum c_i=m\)
類似的計算\(1-z\)的限制,如果小數點最後不為\(0\)的位置為\(len\),則\(1-z\)的\(\sum c'_i=len(k-1)-\sum c_i+1\)。
最終問題變成了統計多少不同的合法\(\sum c_i\),直接DP解決。
using namespace std; #include <cstdio> #include <cstring> #include <algorithm> #define N 3005 #define ll long long #define mo 1000000007 int n,m,k; ll x,y; int mxd; int f[N*2][N][2]; int main(){ freopen("justice.in","r",stdin); freopen("justice.out","w",stdout); // freopen("in.txt","r",stdin); scanf("%d%d%d%lld%lld",&m,&n,&k,&x,&y); if (x==y){ printf("1\n"); return 0; } mxd=(m+n-1)/(k-1); f[0][0][0]=1; for (int i=0;i<mxd;++i) for (int j=0;j<m && j<=i*(k-1);++j) for (int c=0;c<=1;++c){ if (!f[i][j][c]) continue; for (int t=0;t<k;++t) (f[i+1][j+t][t!=0]+=f[i][j][c])%=mo; } ll ans=0; for (int i=1;i<=mxd;++i) for (int j=1;j<=m && j<=i*(k-1);++j){ int j_=i*(k-1)-j+1; if ((m-j)%(k-1)==0 && j_<=n && (n-j_)%(k-1)==0) ans+=f[i][j][1]; } ans%=mo; printf("%lld\n",ans); return 0; }