1. 程式人生 > >LOJ #2541「PKUWC2018」獵人殺

LOJ #2541「PKUWC2018」獵人殺

這樣$ PKUWC$就只差一道鬥地主了

假裝補題補完了吧.....

這題還是挺巧妙的啊......

LOJ # 2541

題意:每個人有一個嘲諷值$a_i$,每次殺死一個人,殺死某人的概率為$ \frac{a_i}{a_{alive}}$,求第一個人最後死的概率

資料範圍:$ 1 \leq a_i \leq 10^5,\sum\limits_{i=1}^n a_i \leq 10^5$


以下部分用$ val$表示所有人的嘲諷值之和

先講講$ n*val$的$ DP$

用$ P_S$表示集合$ S$中的人都在$ 1$後面死的概率

由於期間打死其他人不會影響$ P_S$的結果

每個$ P_S$是獨立的

等價與下一槍打在$ S$或$1$上打死$1$的概率即$ \frac{a_i}{a_S+a_i}$

其中$ a_S$表示集合$ S$的嘲諷值之和

則答案為$ \sum\limits(-1)^{|S|+1}P_S$

容易發現列舉集合的複雜度過大無法承受

發現$ val$不大

嘗試用$ f_{i,j}$表示前$ i$個人(從2開始列舉)嘲諷值之和為$ j$的方案數

發現有容斥係數不能直接記錄方案

不過沒有關係,由於容斥係數只和奇偶性有關,我們只需要把$ f_{i,j}$改成嘲諷值之和為$j$的係數和即可

轉移的時候分選$ i$和不選$i$兩種

如果選了前面的奇偶性會全部改變

因此得出轉移方程式$ f_{i,j}=f_{i-1,j}-f_{i-1,j-a[i]}$

可以過$ 50$分


考慮生成函式

發現轉移的本質是若干個形如$ -x^{a_i}+1$的二項式相乘

即最終轉移結果為 $\prod\limits_{i=2}^n -x^{a_i}+1$

用$ NTT$分治計算這個過程

由於類似線段樹結構的分治只有$ log_n$層,每層的複雜度是$ O(val log val)$

因此總複雜度是$ O(val log n log val)$的,可以通過本題

以及還有$一個log$的小$ trick$

暫時還不會....以後再寫吧


 

$ my \ code:$

#include<ctime>
#include
<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define p 998244353 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x = 0; char zf = 1; char ch = getchar(); while (ch != '-' && !isdigit(ch)) ch = getchar(); if (ch == '-') zf = -1, ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int i,j,k,m,n,x,y,z,cnt; int a[100010]; int ksm(int x,int y){ int ans=1; for(rt i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p; return ans; } namespace poly{ vector<int>R; void getR(int n){ R.resize(n); for(rt i=1;i<n;i++)R[i]=(R[i>>1]>>1)|(i&1)*(n>>1); } void NTT(int n,vector<int>&A,int fla){ for(rt i=0;i<n;i++)if(i>R[i])swap(A[i],A[R[i]]); for(rt i=1;i<n;i<<=1){ int w=ksm(3,(p-1)/2/i); for(rt j=0;j<n;j+=i<<1){ int K=1; for(rt k=0;k<i;k++,K=1ll*K*w%p){ int x=A[j+k],y=1ll*K*A[i+j+k]%p; A[j+k]=(x+y)%p,A[i+j+k]=(x-y)%p; } } } if(fla==-1){ reverse(A.begin()+1,A.end());int invn=ksm(n,p-2); for(rt i=0;i<n;i++)A[i]=1ll*A[i]*invn%p; } } } using namespace poly; int calc(int L,int R,vector<int>&A){ if(L==R){ A.resize(a[L]+1); A[0]=1;A[a[L]]=-1; return a[L]; } const int mid=L+R>>1; vector<int>f,g; int len=calc(L,mid,f)+calc(mid+1,R,g); int lim=1;while(lim<=len)lim<<=1; getR(lim);f.resize(lim);g.resize(lim);A.resize(lim); NTT(lim,f,1);NTT(lim,g,1); for(rt i=0;i<lim;i++)A[i]=1ll*f[i]*g[i]%p; NTT(lim,A,-1); return len; } int main(){ n=read(); for(rt i=1;i<=n;i++)a[i]=read(); vector<int>xs; int sum=calc(2,n,xs);int ans=0; for(rt i=0;i<=sum;i++)(ans+=1ll*xs[i]*ksm(i+a[1],p-2)%p*a[1]%p)%=p; cout<<(ans+p)%p; return 0; }