1. 程式人生 > >「PKUWC2018」Slay the Spire loj2538 「PKUWC 2018」Slay the Spire

「PKUWC2018」Slay the Spire loj2538 「PKUWC 2018」Slay the Spire

loj2538 「PKUWC 2018」Slay the Spire

對於這種題,感覺可以貪心

所以要先手玩,看看有沒有最優決策

發現,如果k張,必定先打強化

關鍵的話是:“每個強化牌翻的倍數大於1”

意味著,多翻一倍少打一張攻擊一定不劣。

如果m張,有i張是強化,選k個,

如果i<k,那麼打i張強化,剩下打攻擊

如果i>=k,打k-1張強化,1張攻擊

發現,決策和分到的強化牌的數量有關係

從這方面dp

F(i,j)表示,分到i張強化,打出j張,所有的翻的倍率(卡牌乘積)的和

G(i,j)表示,分到i張攻擊,打出j張,所有方案的(原始)傷害的和

那麼答案就是:F(i,i)*G(m-i,k-i)和F(i,k-1)*G(m-i,1)(乘法分配律)

考慮怎麼求F,G

有了一些牌,一定打最大的一些個

所以sort

然後,f[i][j]表示,前i個以i結尾,選擇了j個強化牌所翻的倍率的和

dp,字首和優化即可。

g[i][j]表示,前i個以i結尾,選擇了j個攻擊牌所有方案的傷害的和

同上處理

 

然後列舉強化牌i張,

F,G只需要計算n次,所以不用預處理。每次暴力求,列舉最後一個選擇的是誰,剩下位置組合數來搞。

注意,f[i][0]是C(n,i),象徵選擇的方案數,因為乘法的單位元是1,不能沒有。

沒有強化牌認為翻1倍。

而g[i][0]就是0啦。

#include<bits/stdc++.h>
#define
reg register int #define il inline #define numb (ch^'0') #define int long long using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true
)&&(x=-x); } namespace Miracle{ const int N=3003; const int mod=998244353; int f[N][N],g[N][N],c[N][N]; int s[N],t[N]; int a[N],b[N]; int n,m,k; void clear(){ } bool cmp(int x,int y){ return x>y; } int F(int x,int y){ if(x<y) return 0; //if(x==0&&y==0) return 1; if(!y) return c[n][x]; int ret=0; for(reg i=y;i<=n;++i){ ret=(ret+(ll)f[i][y]*c[n-i][x-y])%mod; } return ret; } int G(int x,int y){ if(x<y) return 0; //if(x==0||y==0) return 0; int ret=0; for(reg i=y;i<=n;++i){ ret=(ret+(ll)g[i][y]*c[n-i][x-y])%mod; } return ret; } int main(){ int T; //while(1); rd(T); c[0][0]=1; for(reg i=1;i<=3000;++i){ c[i][0]=1; for(reg j=1;j<=i;++j){ c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } } while(T--){ rd(n);rd(m);rd(k); clear(); for(reg i=1;i<=n;++i) rd(a[i]); for(reg j=1;j<=n;++j) rd(b[j]); sort(a+1,a+n+1,cmp);sort(b+1,b+n+1,cmp); memset(s,0,sizeof s); memset(t,0,sizeof t); t[0]=1; s[0]=1; for(reg i=1;i<=n;++i){ for(reg j=i;j>=1;--j){ f[i][j]=(ll)a[i]*s[j-1]%mod; } for(reg j=i;j>=1;--j){ s[j]=(s[j]+f[i][j])%mod; } } memset(s,0,sizeof s); memset(t,0,sizeof t); t[0]=1; for(reg i=1;i<=n;++i){ for(reg j=i;j>=1;--j){ g[i][j]=(s[j-1]+(ll)b[i]*c[i-1][j-1])%mod; } for(reg j=i;j>=1;--j){ (t[j]+=t[j-1])%=mod; (s[j]+=g[i][j])%=mod; } } // for(reg i=1;i<=n;++i){ // cout<<" ii "<<a[i]<<endl; // for(reg j=1;j<=i;++j){ // cout<<" i j "<<i<<" "<<j<<" ff "<<f[i][j]<<" gg "<<g[i][j]<<endl; // } // } ll ans=0; for(reg i=0;i<m;++i){ if(i<k){ ans=(ans+(ll)F(i,i)*G(m-i,k-i)%mod)%mod; }else{ ans=(ans+(ll)F(i,k-1)*G(m-i,1)%mod)%mod; } } printf("%lld\n",ans); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/1/9 7:21:30 */

手玩出規律

列出狀態

然後乘法分配律得到答案

然後求F,G