1. 程式人生 > >bzoj2655calc 容斥+dp

bzoj2655calc 容斥+dp

geo spa 方案 復雜 algo hint long 都是 scu

2655: calc

Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 322 Solved: 197
[Submit][Status][Discuss]

Description

  一個序列a1,...,an是合法的,當且僅當:
  長度為給定的n。
  a1,...,an都是[1,A]中的整數。
  a1,...,an互不相等。
  一個序列的值定義為它裏面所有數的乘積,即a1a2...an。
  求所有不同合法序列的值的和。
  兩個序列不同當且僅當他們任意一位不一樣。
  輸出答案對一個數mod取余的結果。

Input

  一行3個數,A,n,mod。意義為上面所說的。

Output

  一行結果。

Sample Input

9 7 10007

Sample Output

3611

HINT

數據規模和約定
  0:A<=10,n<=10。
  1..3:A<=1000,n<=20.
  4..9:A<=10^9,n<=20
  10..19:A<=10^9,n<=500。
  全部:mod<=10^9,並且mod為素數,mod>A>n+1

容斥法:
推薦blog
http://blog.csdn.net/qq_20669971/article/details/52790835
有一點不是很懂,就是那個統計f數組時階乘那裏

暴力法。

f[i][j]表示前i個格子,第i個格子填<=j的數的方案數
f[i][j]=f[i-1][j-1]*j+f[i][j-1] 復雜度O(nA)

第二維枚舉A是肯定要TLE的,考慮優化
可以觀察出這個東西可以表示成一個最高次為2n的多項式,未知數為j
那麽就可以用拉格朗日求啦

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
#define N 605
using namespace std;
ll inv[N],c[N][N],fac[N],g[N],f[N],A,n,mod;
int main(){
    cin>>A>>n>>mod;
    fac[
0]=1;inv[1]=1; for(int i=1;i<=510;i++) fac[i]=(fac[i-1]*i)%mod; for(int i=2;i<=510;i++) inv[i]=(1ll*(mod-mod/i)*inv[mod%i])%mod; for(int i=0;i<=n;i++)c[i][i]=c[i][0]=1; for(int i=1;i<=510;i++) for(int j=1;j<i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod; g[0]=(A+1)%mod;g[1]=(1ll*A*(A+1)>>1)%mod; ll t=(A+1)*(A+1)%mod; for(int i=2;i<=n;i++){ t=((A+1)*t)%mod; ll sum=(A+1)%mod; for(int j=1;j<i;j++) sum=(sum+1ll*c[i+1][j]*g[j]%mod)%mod; sum=(t-sum)%mod; sum<0?sum+=mod:1; g[i]=(sum*inv[i+1])%mod; } f[0]=1;f[1]=(1ll*(A+1)*A>>1)%mod; for(int i=2;i<=n;i++){ f[i]=g[1]*f[i-1]%mod; ll fg=-1; for(int j=i-2;~j;j--){ f[i]=(f[i]+1ll*fg*fac[i-1-j]%mod*c[i-1][i-1-j]%mod*g[i-j]%mod*f[j]%mod+mod)%mod; fg=-fg; } } cout<<f[n]; return 0; }

bzoj2655calc 容斥+dp