1. 程式人生 > >[BZOJ 5093] 圖的價值

[BZOJ 5093] 圖的價值

5093: [Lydsy1711月賽]圖的價值

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 544  Solved: 273
[Submit][Status][Discuss]

Description

“簡單無向圖”是指無重邊、無自環的無向圖(不一定連通)。 一個帶標號的圖的價值定義為每個點度數的k次方的和。 給定n和k,請計算所有n個點的帶標號的簡單無向圖的價值之和。 因為答案很大,請對998244353取模輸出。  

Input

第一行包含兩個正整數n,k(1<=n<=10^9,1<=k<=200000)。  

Output

 輸出一行一個整數,即答案對998244353取模的結果。

 

Sample Input

6 5

Sample Output

67584000

開局一個式子, 裝備全靠混凝土

首先我們發現貢獻可以非常容易地根據每個點的情況分開來算, 我們有:
$$
F(n)=n\sum_{i=1}^{n-1}{n-1 \choose i}i^k2^{{n\choose 2}-(n-1)}
$$
含義就是列舉某個點的度數, 算出和它相關的連邊方案, 其他邊隨便連. 點和點之間等價, 直接乘 $n$.

接著我們發現和式內部有些和 $i$ 無關的東西, 我們把它扥出來:
$$
F(n)=n2^{{n\choose 2}-(n-1)}\sum_{i=1}^{n-1}{n-1\choose i}i^k
$$
前面部分顯然非常好求, 我們把重點放在後面那個和式上, 設:
$$
G(n)=\sum_{i=1}^n{n\choose i}i^k
$$
這個求和式的項數是和 $n$ 有關的, 然而 $n$ 巨大無比根本跑不出來...只有一個 $k$ 的資料範圍還有救...

我們考慮如何把求和上界向 $k$ 的方向轉化.

翻閱混凝土數學, 我們找到了第二類Stirling反演公式 $(6.10)$:
$$
x^k=\sum_{i=0}^k\begin{Bmatrix}k\\i\end{Bmatrix}x^{\underline i}
$$
扔進去試試看:
$$
G(n)=\sum_{i=1}^n{n\choose i}\sum_{r=0}^k\begin{Bmatrix}k\\r\end{Bmatrix}i^{\underline r}
$$
按照求和式套路把所有東西放在最裡層, 對外層求和指標亂搞再把和裡層指標無關的東西抻回來:
$$
\begin{aligned}
G(n)&=\sum_{i=1}^n\sum_{r=0}^k{n\choose i}\begin{Bmatrix}k\\r\end{Bmatrix}i^{\underline r} \\
&=\sum_{r=0}^k\sum_{i=1}^n{n\choose i}\begin{Bmatrix}k\\r\end{Bmatrix}i^{\underline r} \\
&=\sum_{r=0}^k\begin{Bmatrix}k\\r\end{Bmatrix}\sum_{i=1}^n{n\choose i}i^{\underline r}
\end{aligned}
$$
我們看那個下降階乘冪在一個二項式係數旁邊就非常不爽, 我們把它換成二項式係數來方便比對公式

:
$$
G(n)=\sum_{r=0}^k\begin{Bmatrix}k\\r\end{Bmatrix}\sum_{i=1}^n{n\choose i}{i\choose r}r!
$$
然後我們果然在混凝土中匹配到了一個公式 $(5.21)$!
$$
{r\choose m}{m\choose k}={r\choose k}{r-k\choose m-k}
$$
代進去化一化:
$$
\begin{aligned}
G(n)&=\sum_{r=0}^k\begin{Bmatrix}k\\r\end{Bmatrix}\sum_{i=1}^n{n\choose r}{n-r\choose i-r}r!\\
&=\sum_{r=0}^k\begin{Bmatrix}k\\r\end{Bmatrix}r!{n\choose r}\sum_{i=1}^n{n-r\choose i-r}\\
&=\sum_{r=0}^k\begin{Bmatrix}k\\r\end{Bmatrix}r!{n\choose r}2^{n-r}
\end{aligned}
$$
完美!

現在問題是怎麼求 $\begin{Bmatrix}k\\r\end{Bmatrix}$. 它可能並沒有什麼封閉形式.

回想 $\begin{Bmatrix}k\\r\end{Bmatrix}$ 到底表示什麼:

符號 $\begin{Bmatrix}n\\k\end{Bmatrix}$ 表示將一個有 $n$ 個物品的集合劃分成 $k$ 個非空子集的方案數

如果沒有集合非空的限制而且集合帶有 1~k 的標號, 那麼我們可以發現答案就是 $k^n$(給 $n$ 個元素分配一個集合標號)

列舉有多少集合是空的, 容斥一下並消序得到:
$$
\begin{Bmatrix}n\\k\end{Bmatrix}=\frac 1 {k!}\sum_{i=0}^k{k\choose i}(-1)^i(k-i)^n
$$
我們發現後面的和式就是數列 $\left \langle (-1)^k \right \rangle$ 和 $\left \langle k^n \right \rangle$ 的二項卷積, 法法塔一發即可 $O(n\log n)$ 求出所有 $\begin{Bmatrix} n \\ k \end{Bmatrix}$.

求完Stirling數回代即可. 複雜度 $O(k\log k)$.

程式碼實現

就用了個多項式乘法還套全家桶真是吃棗藥丸

  1 #include <bits/stdc++.h>
  2 
  3 const int G=3;
  4 const int DFT=1;
  5 const int IDFT=-1;
  6 const int MAXN=550000;
  7 const int MOD=998244353;
  8 const int INV2=(MOD+1)>>1;
  9 const int PHI=MOD-1;
 10 
 11 typedef std::vector<int> Poly;
 12 
 13 Poly Sqrt(Poly);
 14 void Read(Poly&);
 15 Poly Inverse(Poly);
 16 Poly Ln(const Poly&);
 17 Poly Exp(const Poly&);
 18 void Print(const Poly&);
 19 void NTT(Poly&,int,int);
 20 Poly Pow(const Poly&,int);
 21 Poly Integral(const Poly&);
 22 Poly Derivative(const Poly&);
 23 Poly operator*(Poly,Poly);
 24 Poly operator/(Poly,Poly);
 25 Poly operator%(Poly,Poly);
 26 Poly operator+(const Poly&,const Poly&);
 27 Poly operator-(const Poly&,const Poly&);
 28 
 29 int Cn[MAXN];
 30 int rev[MAXN];
 31 int inv[MAXN];
 32 int fact[MAXN];
 33 
 34 int NTTPre(int);
 35 int Sqrt(int,int);
 36 int Pow(int,int,int);
 37 int Log(int,int,int);
 38 int ExGCD(int,int,int&,int&);
 39 
 40 int main(){
 41     int n,k;
 42     scanf("%d%d",&n,&k);
 43     int ans=1ll*n*Pow(2,1ll*(n-1)*(n-2)/2%(MOD-1),MOD)%MOD;
 44     --n;
 45     Poly a(k+1),b(k+1);
 46     fact[0]=a[0]=Cn[0]=1;
 47     for(int i=1;i<=k;i++){
 48         fact[i]=1ll*fact[i-1]*i%MOD;
 49         inv[i]=Pow(fact[i],MOD-2,MOD);
 50         a[i]=1ll*(i&1?MOD-1:1)*inv[i]%MOD;
 51         b[i]=1ll*Pow(i,k,MOD)*inv[i]%MOD;
 52         Cn[i]=1ll*Cn[i-1]*(n-i+1)%MOD*Pow(i,MOD-2,MOD)%MOD;
 53     }
 54     Poly s=a*b;
 55     int g=0;
 56     for(int i=0;i<=k;i++)
 57         (g+=1ll*s[i]*fact[i]%MOD*Cn[i]%MOD*Pow(2,n-i,MOD)%MOD)%=MOD;
 58     ans=1ll*g*ans%MOD;
 59     printf("%d\n",ans);
 60     return 0;
 61 }
 62 
 63 void Read(Poly& a){
 64     for(auto& i:a)
 65         scanf("%d",&i);
 66 }
 67 
 68 void Print(const Poly& a){
 69     for(auto i:a)
 70         printf("%d ",i);
 71     puts("");
 72 }
 73 
 74 Poly Pow(const Poly& a,int k){
 75     Poly log=Ln(a);
 76     for(auto& i:log)
 77         i=1ll*i*k%MOD;
 78     return Exp(log);
 79 }
 80 
 81 Poly Sqrt(Poly a){
 82     int len=a.size();
 83     if(len==1)
 84         return Poly(1,Sqrt(a[0],MOD));
 85     else{
 86         Poly b=a;
 87         b.resize((len+1)>>1);
 88         b=Sqrt(b);
 89         b.resize(len);
 90         Poly inv=Inverse(b);
 91         int bln=NTTPre(inv.size()+a.size());
 92         NTT(a,bln,DFT);
 93         NTT(inv,bln,DFT);
 94         for(int i=0;i<bln;i++)
 95             a[i]=1ll*a[i]*INV2%MOD*inv[i]%MOD;
 96         NTT(a,bln,IDFT);
 97         for(int i=0;i<len;i++)
 98             b[i]=(1ll*b[i]*INV2%MOD+a[i])%MOD;
 99         return b;
100     }
101 }
102 
103 Poly Exp(const Poly& a){
104     size_t len=1;
105     Poly ans(1,1),one(1,1);
106     while(len<(a.size()<<1)){
107         len<<=1;
108         Poly b=a;
109         b.resize(len);
110         ans=ans*(one-Ln(ans)+b);
111         ans.resize(len);
112     }
113     ans.resize(a.size());
114     return ans;
115 }
116 
117 Poly Ln(const Poly& a){
118     Poly ans=Integral(Derivative(a)*Inverse(a));
119     ans.resize(a.size());
120     return ans;
121 }
122 
123 Poly Integral(const Poly& a){
124     int len=a.size();
125     Poly ans(len+1);
126     for(int i=1;i<len;i++)
127         ans[i]=1ll*a[i-1]*Pow(i,MOD-2,MOD)%MOD;
128     return ans;
129 }
130 
131 Poly Derivative(const Poly& a){
132     int len=a.size();
133     Poly ans(len-1);
134     for(int i=1;i<len;i++)
135         ans[i-1]=1ll*a[i]*i%MOD;
136     return ans;
137 }
138 
139 Poly operator/(Poly a,Poly b){
140     int n=a.size()-1,m=b.size()-1;
141     Poly ans(1);
142     if(n>=m){
143         std::reverse(a.begin(),a.end());
144         std::reverse(b.begin(),b.end());
145         b.resize(n-m+1);
146         ans=Inverse(b)*a;
147         ans.resize(n-m+1);
148         std::reverse(ans.begin(),ans.end());
149     }
150     return ans;
151 }
152 
153 Poly operator%(Poly a,Poly b){
154     int n=a.size()-1,m=b.size()-1;
155     Poly ans;
156     if(n<m)
157         ans=a;
158     else
159         ans=a-(a/b)*b;
160     ans.resize(m);
161     return ans;
162 }
163 
164 Poly operator*(Poly a,Poly b){
165     int len=a.size()+b.size()-1;
166     int bln=NTTPre(len);
167     NTT(a,bln,DFT);
168     NTT(b,bln,DFT);
169     for(int i=0;i<bln;i++)
170         a[i]=1ll*a[i]*b[i]%MOD;
171     NTT(a,bln,IDFT);
172     a.resize(len);
173     return a;
174 }
175 
176 Poly operator+(const Poly& a,const Poly& b){
177     Poly ans(std::max(a.size(),b.size()));
178     std::copy(a.begin(),a.end(),ans.begin());
179     for(size_t i=0;i<b.size();i++)
180         ans[i]=(ans[i]+b[i])%MOD;
181     return ans;
182 }
183 
184 Poly operator-(const Poly& a,const Poly& b){
185     Poly ans(std::max(a.size(),b.size()));
186     std::copy(a.begin(),a.end(),ans.begin());
187     for(size_t i=0;i<b.size();i++)
188         ans[i]=(ans[i]+MOD-b[i])%MOD;
189     return ans;
190 }
191 
192 Poly Inverse(Poly a){
193     int len=a.size();
194     if(len==1)
195         return Poly(1,Pow(a[0],MOD-2,MOD));
196     else{
197         Poly b(a);
198         b.resize((len+1)>>1);
199         b=Inverse(b);
200         int bln=NTTPre(b.size()*2+a.size());
201         NTT(a,bln,DFT);
202         NTT(b,bln,DFT);
203         for(int i=0;i<bln;i++)
204             b[i]=(2ll*b[i]%MOD-1ll*b[i]*b[i]%MOD*a[i]%MOD+MOD)%MOD;
205         NTT(b,bln,IDFT);
206         b.resize(len);
207         return b;
208     }
209 }
210 
211 void NTT(Poly& a,int len,int opt){
212     a.resize(len);
213     for(int i=0;i<len;i++)
214         if(rev[i]>i)
215             std::swap(a[i],a[rev[i]]);
216     for(int i=1;i<len;i<<=1){
217         int step=i<<1;
218         int wn=Pow(G,(PHI+opt*PHI/step)%PHI,MOD);
219         for(int j=0;j<len;j+=step){
220             int w=1;
221             for(int k=0;k<i;k++,w=1ll*w*wn%MOD){
222                 int x=a[j+k];
223                 int y=1ll*a[j+k+i]*w%MOD;
224                 a[j+k]=(x+y)%MOD;
225                 a[j+k+i]=(x-y+MOD)%MOD;
226             }
227         }
228     }
229     if(opt==IDFT){
230         int inv=Pow(len,MOD-2,MOD);
231         for(int i=0;i<len;i++)
232             a[i]=1ll*a[i]*inv%MOD;
233     }
234 }
235 
236 int NTTPre(int n){
237     int bln=1,bct=0;
238     while(bln<n){
239         bln<<=1;
240         ++bct;
241     }
242     for(int i=0;i<bln;i++)
243         rev[i]=(rev[i>>1]>>1)|((i&1)<<(bct-1));
244     return bln;
245 }
246 
247 inline int Pow(int a,int n,int p){
248     int ans=1;
249     while(n>0){
250         if(n&1)
251             ans=1ll*a*ans%p;
252         a=1ll*a*a%p;
253         n>>=1;
254     }
255     return ans;
256 }
257 
258 int ExGCD(int a,int b,int& x,int& y){
259     if(b==0){
260         x=1,y=0;
261         return a;
262     }
263     else{
264         int gcd=ExGCD(b,a%b,y,x);
265         y-=x*(a/b);
266         return gcd;
267     }
268 }
269 
270 inline int Sqrt(int a,int p){
271     int s=Pow(G,Log(G,a,p)>>1,p);
272     return std::min(s,MOD-s);
273 }
274 
275 inline int Log(int a,int x,int p){
276     int s=sqrt(p+0.5);
277     int inv=Pow(Pow(a,s,p),p-2,p);
278     std::unordered_map<int,int> m;
279     m[1]=0;
280     int pow=1;
281     for(int i=1;i<s;i++){
282         pow=1ll*a*pow%p;
283         if(!m.count(pow))
284             m[pow]=i;
285     }
286     for(int i=0;i<s;i++){
287         if(m.count(x))
288             return i*s+m[x];
289         x=1ll*x*inv%MOD;
290     }
291     return -1;
292 }
BZOJ 5093

日常圖包