洛谷P4707 重返現世 [DP,min-max容斥]
傳送門
前置知識
做這題前,您需要認識這個式子:
\[ kthmax(S)=\sum_{\varnothing\neq T\subseteq S}{|T|-1\choose k-1} (-1)^{|T|-k} min(T) \]
如果不會可以來這裏。
題解
題目要求第\(k\)小。為了方便,以下令\(k=n-k+1\),即變為求第\(k\)大。
很顯然,這題是讓我們求這個東西:
\[ \sum_{T\neq\varnothing}{|T|-1\choose k-1} (-1)^{|T|-k} min(T) \]
然而\(n \leq 1000\) 的數據很明顯不能暴力枚舉每一個\(T\)。為了優化復雜度,我們考慮一個類似於背包的\(DP\)
設\(f_{x,j,k}\)表示前\(x\)個元素,滿足\(\sum p=j\),以\(k\)為基準的\(\sum_T {|T|-1 \choose k-1} (-1)^{|T|-k}\)的大小。可能你會奇怪為什麽要記錄\(k\),先往後面看。
考慮轉移。顯然要根據\(T\)中是否有\(x\)這個元素進行分類討論。
當\(T\)中沒有\(x\),直接轉移,\(f_{x,j,k}+=f_{x-1,j,k}\)。
當\(T\)中有\(x\)時,顯然前兩維由\(f_{x-1,j-v}(v=p_x)\)轉移而來,有
\[ \begin{align*} f‘_{x,j,k}&=\sum_{x\in T} {|T|-1 \choose k-1}(-1)^{|T|-k}\&=\sum_T {|T| \choose k-1} (-1)^{|T|-k+1}//把x丟掉,轉為考慮x-1時的T,此時\sum_p = j-v\&=\sum_T [{|T|-1 \choose k-1}+{|T|-1 \choose k-2}](-1)^{|T|-k+1}\&=\sum_T {|T|-1 \choose k-1}(-1)^{|T|-k}(-1)+\sum_T {|T|-1 \choose (k-1)-1} (-1)^{|T|-(k-1)}\&=f_{x-1,j-v,k-1}-f_{x-1,j-v,k} \end{align*} \]
最終我們得到轉移方程:
\[ f_{x,j,k}=f_{x-1,j,k}+f_{x-1,j-v,k-1}-f_{x-1,j-v,k} \]
邊界條件為\(f_{x,0,0}=1\)。
發現這東西時空復雜度都是\(O(nm(n-k))\),似乎要炸空間,所以還需要把第一維滾掉。
最後統計答案時枚舉\(\sum p\)然後隨便搞搞就好啦。
代碼
#include<bits/stdc++.h> namespace my_std{ using namespace std; #define pii pair<int,int> #define fir first #define sec second #define MP make_pair #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define drep(i,x,y) for (int i=(x);i>=(y);i--) #define go(x) for (int i=head[x];i;i=edge[i].nxt) #define sz 10010 #define mod 998244353 typedef long long ll; template<typename T> inline void read(T& t) { t=0;char f=0,ch=getchar(); double d=0.1; while(ch>‘9‘||ch<‘0‘) f|=(ch==‘-‘),ch=getchar(); while(ch<=‘9‘&&ch>=‘0‘) t=t*10+ch-48,ch=getchar(); if(ch==‘.‘) { ch=getchar(); while(ch<=‘9‘&&ch>=‘0‘) t+=d*(ch^48),d*=0.1,ch=getchar(); } t=(f?-t:t); } template<typename T,typename... Args> inline void read(T& t,Args&... args){read(t); read(args...);} void file() { #ifndef ONLINE_JUDGE freopen("a.txt","r",stdin); #endif } // inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;} } using namespace my_std; ll ksm(ll x,int y) { ll ret=1; for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod; return ret; } ll inv(ll x){return ksm(x,mod-2);} int n,m,K; int p[sz]; ll dp[2][sz][15]; int main() { file(); read(n,K,m);K=n-K+1; rep(i,1,n) read(p[i]); int c=0,cc=1; dp[0][0][0]=1; rep(i,1,n) { swap(c,cc); rep(j,0,m) rep(k,0,K) dp[c][j][k]=0; dp[c][0][0]=1; rep(j,1,p[i]-1) rep(k,1,K) dp[c][j][k]=dp[cc][j][k]; rep(j,p[i],m) rep(k,1,K) dp[c][j][k]=(dp[cc][j][k]+dp[cc][j-p[i]][k-1]-dp[cc][j-p[i]][k]+mod)%mod; } ll ans=0; rep(i,1,m) ans=(ans+dp[c][i][K]*inv(i)%mod*m%mod)%mod; cout<<ans; return 0; }
洛谷P4707 重返現世 [DP,min-max容斥]