公式:
S , T S,T S,T为两个非空数集.
max ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 min ( T ) \max(S)=\sum_{T\subseteq S} (-1)^{|T|-1} \min(T) max(S)=T⊆S∑(−1)∣T∣−1min(T)
min ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 max ( T ) \min(S)=\sum_{T\subseteq S}(-1)^{|T|-1}\max(T) min(S)=T⊆S∑(−1)∣T∣−1max(T)
证明:
方法1:
我们都知道容斥原理:
∣ ∪ i = 1 n A i ∣ = ∑ j = 0 n ( − 1 ) j + 1 ( ∑ 1 ≤ a 1 < a 2 . . . < a j ≤ n ∣ A a 1 ∩ A a 2 . . . ∩ A a j ∣ ) (1) |\cup_{i=1}^n A_i| =\sum_{j=0}^n (-1)^{j+1} \left(\sum\limits_{1\le a_1
∣ ∩ i = 1 n A i ∣ = ∑ j = 0 n ( − 1 ) j ( ∑ 1 ≤ a 1 < a 2 . . . < a j ≤ n ∣ A a 1 ‾ ∩ A a 2 ‾ . . . ∩ A a j ‾ ∣ ) (2) |\cap_{i=1}^n A_i|=\sum_{j=0}^n (-1)^{j} \left( \sum\limits_{1\le a_1
我们设 A i = { 1 , 2 , . . . , i } A_i=\{1,2,...,i\} Ai={1,2,...,i}.
定义集族 S ′ S' S′,若 x ∈ S x\in S x∈S,则有 A x ∈ S ′ A_x\in S' Ax∈S′.
因为 ∣ A x ∪ A y ∣ = max ( x , y ) , ∣ A x ∩ A y ∣ = min ( x , y ) |A_x\cup A_y|=\max(x,y),|A_x\cap A_y|=\min(x,y) ∣Ax∪Ay∣=max(x,y),∣Ax∩Ay∣=min(x,y).
所以 max ( S ) = ∣ ∪ x ∈ S A x ∣ = ∑ j = 1 n ( − 1 ) j + 1 ( ∑ ∣ ∩ k = 1 j A a k ∣ ) = ∑ T ∈ S ( − 1 ) ∣ T ∣ + 1 min ( T ) \max(S)=|\cup_{x\in S}~ A_x|=\sum_{j=1}^n (-1)^{j+1} \left( \sum |\cap_{k=1}^j A_{a_k}|\right)=\sum_{T\in S}(-1)^{|T|+1}\min(T) max(S)=∣∪x∈S Ax∣=∑j=1n(−1)j+1(∑∣∩k=1jAak∣)=∑T∈S(−1)∣T∣+1min(T).
同理,我们把每个数的大小翻转 x = − x x=-x x=−x,那么最大值和最小值就互换了,也就可以得到第二个式子.
方法2:
参考资料
上面的证明有一定的局限性,因为是只能作用于整数域.
max ( S ) = ∑ T ∈ S ( − 1 ) ∣ T ∣ − 1 min ( T ) \max(S)=\sum_{T\in S} (-1)^{|T|-1} \min(T) max(S)=∑T∈S(−1)∣T∣−1min(T)
那么第 k k k小的数的贡献为 ∑ j = 0 n − k ( − 1 ) j ( n − k j ) = 0 n − k = [ n = k ] \sum_{j=0}^{n-k} (-1)^j \dbinom {n-k} j=0^{n-k}=[n=k] ∑j=0n−k(−1)j(jn−k)=0n−k=[n=k].
所以公式显然成立.
大小翻转 x = − x x=-x x=−x可得公式二.
min − max \min-\max min−max容斥的套路是化 max \max max为 min \min min,因为 min \min min一般更好求.
我们定义 m a x ( S ) , m i n ( S ) max(S),min(S) max(S),min(S)分别表示取到 S S S的最大,最小时间,也就是一个 S S S对应一个时间集合.
min ( T ) = 1 ∑ i ∈ T p i \min(T)=\dfrac 1 {\sum_{i\in T} p_i} min(T)=∑i∈Tpi1,求一个的概率还是很简单的~~
#include
using namespace std;
const int N=22;
int n,g[1<<N];
double ans,f[1<<N];
int main() {
while(~scanf("%d",&n)) {
for(int i=0;i<n;i++) scanf("%lf",&f[1<<i]);
g[0]=-1; ans=0;
for(int i=1;i<(1<<n);i++) {
f[i]=f[i&(i-1)]+f[i&-i];
g[i]=-g[i&(i-1)];
ans += g[i]/f[i];
}
printf("%.8lf\n",ans);
}
return 0;
}
定义 m i n ( T ) min(T) min(T)为取到 T T T集合中任意一位的最小时间, m a x ( S ) max(S) max(S)为最大时间.
m i n ( T ) = 1 ∑ S ∩ T ≠ ∅ p S = 1 1 − f w t [ T ‾ ] min(T)=\dfrac 1{\sum_{S\cap T\ne \varnothing} p_S}=\dfrac 1 {1-fwt[\overline T]} min(T)=∑S∩T=∅pS1=1−fwt[T]1
#include
using namespace std;
const int N=(1<<20)|10;
const double eps=1e-9;
int n,g[N];
double f[N],ans;
void fwt(double *f) {
for(int k=1;k<n;k*=2)
for(int i=0;i<n;i+=2*k)
for(int j=0;j<k;j++)
f[i+j+k] += f[i+j];
}
int main() {
scanf("%d",&n); n=1<<n;
for(int i=0;i<n;i++) scanf("%lf",&f[i]);
fwt(f); g[0]=-1;
for(int i=1;i<n;i++) {
g[i]=-g[i&(i-1)];
if(fabs(f[i^(n-1)]-1)<eps)
{puts("INF"); return 0;}
ans += g[i]/(1-f[i^(n-1)]);
}
printf("%.10lf\n",ans);
return 0;
}
给定一棵 n n n 个结点的树,你从点 x x x 出发,每次等概率随机选择一条与所在点相邻的边走过去。
有 q q q次询问,每次询问给定一个集合 ,求如果从 x x x出发一直随机游走,直到点集 S S S中所有点都至少经过一次的话,期望游走几步。
特别地,点 x x x(即起点)视为一开始就被经过了一次。
答案对 998244353 取模。
我们对于给定的 S S S,定义 f x f_x fx表示 x x x到 S S S一点的期望时间,则 min ( S ) = f r o o t \min(S)=f_{root} min(S)=froot.
转移方程: f x = f f a + ∑ y ∈ s o n x f y d e g [ x ] + 1 f_x=\dfrac{f_{fa} + \sum_{y\in son_x} f_y}{deg[x]} +1 fx=deg[x]ffa+∑y∈sonxfy+1.
为了避免后效性,我们定义 f x = A x ⋅ f f a + B x f_x=A_x\cdot f_{fa} + B_x fx=Ax⋅ffa+Bx.
则有: f x = f f a + ∑ y ∈ s o n x A y ⋅ f x + B y d e g [ x ] + 1 f_x=\dfrac{f_{fa} + \sum_{y\in son_x} A_y\cdot f_x+B_y}{deg[x]} + 1 fx=deg[x]ffa+∑y∈sonxAy⋅fx+By+1.
把和 f ( x ) f(x) f(x)有关的系数移动到左边,然后消去系数,得到:
f x = f f a d e g x − ∑ A y + d e g x + ∑ B y d e g x − ∑ A y → A x = 1 d e g x − ∑ A y , B x = d e g x + ∑ B y d e g x − ∑ A y f_x=\dfrac{f_{fa}}{deg_x-\sum A_y}+\dfrac{deg_x+\sum B_y}{deg_x-\sum A_y}\rightarrow A_x=\dfrac 1 {deg_x-\sum A_y}, B_x=\dfrac{deg_x+\sum B_y}{deg_x-\sum A_y} fx=degx−∑Ayffa+degx−∑Aydegx+∑By→Ax=degx−∑Ay1,Bx=degx−∑Aydegx+∑By.
特别的,对于 S S S内的点 x x x, f x = A x = B x = 0 f_x=A_x=B_x=0 fx=Ax=Bx=0.
然后就是一个子集和的问题了,显然 O ( q ∗ 3 n ) O(q*3^n) O(q∗3n)会T,优化一下可以做到 O ( q ⋅ 2 n ⋅ n ) O(q\cdot 2^n \cdot n) O(q⋅2n⋅n).
如果我们全部预处理一般的话,复杂度即为 O ( n 2 n + ∑ k ) O(n2^n+\sum k) O(n2n+∑k).
int n,q,rt,cnt[M],f[M],fa[N];
struct edge{int y,next;} a[N*2]; int len,last[N],deg[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len; deg[x]++;}
struct rec {
ll x,y;
void operator *=(ll t) {x=x*t%mod; y=y*t%mod;}
void operator +=(rec t) {x=(x+t.x)%mod; y=(y+t.y)%mod; }
} ;
ll power(ll a,ll b=mod-2) {
ll c=1;
while(b) {
if(b&1) c=c*a%mod;
b /= 2; a=a*a%mod;
}
return c;
}
rec dfs(int x,int S) {
rec now=(rec){0,0};
if(S>>x&1) return now;
for(int k=last[x],y;k;k=a[k].next)
if((y=a[k].y)^fa[x]) {
fa[y]=x;
now += dfs(y,S);
}
ll t=power(deg[x]-now.x+mod);
now.x=t; now.y=(now.y+deg[x])*t%mod;
return now;
}
int main() {
qr(n); qr(q); qr(rt); rt--;
for(int i=1,x,y;i<n;i++)
qr(x),qr(y),--x,--y,ins(x,y),ins(y,x);
fa[rt]=cnt[0]=-1;
for(int i=1;i<(1<<n);i++)
cnt[i]=-cnt[i&(i-1)],f[i]=(cnt[i]+mod)*dfs(rt,i).y%mod;
for(int i=1;i<(1<<n);i*=2)
for(int j=i;j<(1<<n);j++) if(j&i) f[j]=(f[j]+f[i^j])%mod;
while(q--) {
int k,x=0,y; qr(k);
while(k--) qr(y),x|=1<<(--y);
pr2(f[x]);
}
return 0;
}
给定一个长度为 n n n的序列 a a a,求 l c m ( f a 1 , f a 2 , . . . , f a n ) m o d ( 1 0 9 + 7 ) lcm(f_{a_1},f_{a_2},...,f_{a_n})\mod (10^9+7) lcm(fa1,fa2,...,fan)mod(109+7),其中 f f f为斐波那契数列. n ≤ 5 e 4 , a i ≤ 1 e 6 n\le 5e4,a_i\le 1e6 n≤5e4,ai≤1e6.
神仙题,膜题解才会.
通过这题 get 一个斐波那契数列的性质: gcd ( f n , f m ) = f gcd ( n , m ) \gcd(f_n,f_m)=f_{\gcd(n,m)} gcd(fn,fm)=fgcd(n,m).
由于 l c m , g c d lcm,gcd lcm,gcd分别是指数的 max , min \max,\min max,min运算,不妨把他们用 min − max \min-\max min−max容斥的方法连起来.
max ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ + 1 min ( T ) \max(S)=\sum_{T\subseteq S} (-1)^{|T|+1} \min(T) max(S)=∑T⊆S(−1)∣T∣+1min(T)
对于同一个质因子的话,则有 l c m ( S ) = ∏ T ⊆ S gcd ( T ) ( − 1 ) ∣ T ∣ + 1 ( x ∈ S → p ∣ x ) lcm(S)=\prod_{T\subseteq S} \gcd(T)^{(-1)^{|T|+1}}(x\in S\rightarrow p|x) lcm(S)=∏T⊆Sgcd(T)(−1)∣T∣+1(x∈S→p∣x).
我们把所有质因子的情况乘起来就是总的情况了:$ lcm(S)=\prod_{T\subseteq S} \gcd(T){(-1){|T|+1}}$.
定义 f S f_S fS表示 { f x ∣ x ∈ S } \{f_x|x\in S \} {fx∣x∈S},这里的 S S S为 a a a.
s o a n s = l c m ( f S ) = ∏ T ⊆ S g c d ( f T ) ( − 1 ) ∣ T ∣ + 1 = ∏ T ⊆ S f g c d ( T ) ( − 1 ) ∣ T ∣ + 1 so~~ans=lcm(f_S)=\prod_{T\subseteq S} gcd(f_T)^{(-1)^{|T|+1}}=\prod_{T\subseteq S} f_{gcd(T)}^{(-1)^{|T|+1}} so ans=lcm(fS)=∏T⊆Sgcd(fT)(−1)∣T∣+1=∏T⊆Sfgcd(T)(−1)∣T∣+1.
我们不可能枚举 T , 求 gcd ( T ) T,求\gcd(T) T,求gcd(T),因为这是 2 n n 2^n n 2nn级别的暴力.
考虑每个位置 d d d 的影响, 那么显然我们有一个 ∑ [ g c d ( T ) = d ] ( − 1 ) ∣ T ∣ + 1 \sum[gcd(T)=d](-1)^{|T|+1} ∑[gcd(T)=d](−1)∣T∣+1 的指数.
有 gcd \gcd gcd 我们想到什么,没错就是我们的莫比乌斯反演,不过是在 ∏ \prod ∏ 意义下哦.
f = g ∗ 1 ⇔ g = f ∗ μ , f n = ∏ d ∣ n g d ⇔ g n = ∏ d ∣ n f d μ ( n / d ) f=g*1\Leftrightarrow g=f*\mu,f_n=\prod_{d|n} g_d\Leftrightarrow g_n=\prod_{d|n} f_d^{\mu(n/d)} f=g∗1⇔g=f∗μ,fn=∏d∣ngd⇔gn=∏d∣nfdμ(n/d).
加法转乘法可以类比 指数加法 和 乘法 来理解.(不过我们下面并没有用到反演公式)
我们把其他约数的影响去掉即可得到 g n g_n gn.
式子变成这个丫子:
∏ T ⊆ S f g c d ( T ) ( − 1 ) ∣ T ∣ + 1 = ∏ T ⊆ S ( ∏ d ∣ g c d ( T ) g d ) ( − 1 ) ∣ T ∣ + 1 = ∏ d g ( d ) ∑ d ∣ g c d ( T ) ( − 1 ) ∣ T ∣ + 1 \prod_{T\subseteq S} f_{gcd(T)}^{(-1)^{|T|+1}}=\prod_{T\subseteq S} \left( \prod_{d|gcd(T)} g_d \right) ^{(-1)^{|T|+1}}= \prod_d g(d)^{\sum_{d|gcd(T)}(-1)^{|T|+1}} ∏T⊆Sfgcd(T)(−1)∣T∣+1=∏T⊆S(∏d∣gcd(T)gd)(−1)∣T∣+1=∏dg(d)∑d∣gcd(T)(−1)∣T∣+1.
考虑每个 d d d的贡献,设 d d d的倍数有 n n n个,则有:
∑ i = 1 n ( n i ) ( − 1 ) i + 1 = ( ∑ i = 0 n ( n i ) ( − 1 ) i + 1 ) + 1 = 1 − 0 n = [ n > 0 ] \sum_{i=1}^n \dbinom n i (-1)^{i+1}=\left(\sum_{i=0}^n \dbinom n i (-1)^{i+1} \right) +1=1-0^{n}=[n>0] ∑i=1n(in)(−1)i+1=(∑i=0n(in)(−1)i+1)+1=1−0n=[n>0].
也就是说,只要有一个 d d d的倍数,那么 g d g_d gd就会被计算一次.
综上,用个埃氏筛求 g g g并判断倍数有没有即可.总复杂度为 O ( n + m log m ) ( m = max a i ) O(n+m\log m)(m=\max a_i) O(n+mlogm)(m=maxai)
#include
using namespace std;
typedef long long ll;
const int N=1e6+10,mod=1000000007;
int n,g[N],m;
bool v[N];
ll power(ll a,ll b=mod-2) {
ll c=1;
while(b) {
if(b&1) c=c*a%mod;
b /= 2; a=a*a%mod;
}
return c;
}
void qr(int &x){scanf("%d",&x);}
int main() {
qr(n);
for(int i=1,x;i<=n;i++) qr(x),v[x]=1,m=max(m,x);
g[1]=1; for(int i=2;i<=m;i++) g[i]=(g[i-1]+g[i-2])%mod;
for(int i=1;i*2<=m;i++) {
ll k=power(g[i]);
for(int j=2*i;j<=m;j+=i)
g[j]=g[j]*k%mod;
}
ll ans=1;
for(int i=1;i<=m;i++)
for(int j=i;j<=m;j+=i)
if(v[j]) {ans=ans*g[i]%mod; break;}
printf("%lld\n",ans);return 0;
}
设 ( 1 + 2 ) n = a n + 2 b n , g n = l c m ( b 1 , b 2 , . . . , b n ) (1+\sqrt 2)^n=a_n+\sqrt 2 b_n,g_n=lcm(b_1,b_2,...,b_n) (1+2)n=an+2bn,gn=lcm(b1,b2,...,bn),求 ∑ i = 1 n i g ( i ) m o d p \sum_{i=1}^n {ig(i)}\mod p ∑i=1nig(i)modp. ( n ≤ 1 e 6 , p ≤ 1 e 9 + 7 ) (n\le 1e6,p\le 1e9+7) (n≤1e6,p≤1e9+7)
我们可以推一下公式:
a n = a n − 1 + 2 ∗ b n − 1 a_n=a_{n-1}+2*b_{n-1} an=an−1+2∗bn−1,
b n = a n − 1 + b n − 1 = a n − 2 + 2 ∗ b n − 2 + b n − 1 = 1 + b n − 1 + ∑ i = 1 n − 2 2 b i b_n=a_{n-1}+b_{n-1}=a_{n-2}+2*b_{n-2}+b_{n-1}=1+b_{n-1}+\sum_{i=1}^{n-2}2b_i bn=an−1+bn−1=an−2+2∗bn−2+bn−1=1+bn−1+∑i=1n−22bi.
差分得到: b n − b n − 1 = b n − 1 + 2 b n − 2 − b n − 2 = b n − 1 + b n − 2 → b n = 2 b n − 1 + b n − 2 b_n-b_{n-1}=b_{n-1}+2b_{n-2}-b_{n-2}=b_{n-1}+b_{n-2}\rightarrow b_n=2b_{n-1}+b_{n-2} bn−bn−1=bn−1+2bn−2−bn−2=bn−1+bn−2→bn=2bn−1+bn−2.(其实不推也罢)
然后,我们猜个结论 g c d ( b x , b y ) = b gcd ( x , y ) gcd(b_x,b_y)=b_{\gcd(x,y)} gcd(bx,by)=bgcd(x,y),结果用 _ _ i n t 128 \_\_int128 __int128跑了 n = 100 n=100 n=100范围内,发现结论正确.
那么这道题好像就和上一题没啥区别了:
l c m ( f S ) = ∏ T ⊆ S g c d ( f T ) ( − 1 ) ∣ T ∣ + 1 = ∏ T ⊆ S f gcd ( T ) ( − 1 ) ∣ T ∣ + 1 . . . = ∏ d = 1 n g i lcm(f_S)=\prod_{T\subseteq S} gcd(f_T)^{(-1)^{|T|+1}}=\prod_{T\subseteq S}f_{\gcd(T)}^{(-1)^{|T|+1}}...=\prod_{d=1}^n g_i lcm(fS)=∏T⊆Sgcd(fT)(−1)∣T∣+1=∏T⊆Sfgcd(T)(−1)∣T∣+1...=∏d=1ngi.
我们解出 g g g后就差不多做完了…
最后证明一下瞎猜结论的正确性吧:
有个结论, f 0 = 0 , f 1 = 1 , a ⊥ b , f i = a ∗ f i − 1 + b ∗ f i − 2 → gcd ( f x , f y ) = f gcd ( x , y ) f_0=0,f_1=1,a\bot b,f_i=a*f_{i-1} + b*f_{i-2} \rightarrow \gcd(f_x,f_y)=f_{\gcd(x,y)} f0=0,f1=1,a⊥b,fi=a∗fi−1+b∗fi−2→gcd(fx,fy)=fgcd(x,y)
引理1: ∀ i ∈ N , b ⊥ f i \forall i\in \N,b \bot f_i ∀i∈N,b⊥fi.证明:前两项显然成立,然后 f i ≡ a ∗ f i − 1 ( m o d b ) f_i\equiv a*f_{i-1} (\mod b) fi≡a∗fi−1(modb),因为 a ⊥ b , f i − 1 ⊥ b a\bot b,f_{i-1}\bot b a⊥b,fi−1⊥b,所以可以归纳,结论正确.
引理2: f n ⊥ f n + 1 f_n \bot f_{n+1} fn⊥fn+1,证明: gcd ( f n , f n + 1 ) = gcd ( f n , b ∗ f n − 1 ) = gcd ( f n , f n − 1 ) . . . = gcd ( f 1 , f 2 ) = 1 \gcd(f_n,f_{n+1})=\gcd(f_n,b*f_{n-1})=\gcd(f_n,f_{n-1})...=\gcd(f_1,f_2)=1 gcd(fn,fn+1)=gcd(fn,b∗fn−1)=gcd(fn,fn−1)...=gcd(f1,f2)=1
容易推出 f m = f n ∗ f m − ( n + 1 ) + f n + 1 ∗ f m − n → gcd ( f m , f n ) = gcd ( f n , f n + 1 ∗ f m − n ) = gcd ( f n , f m − n ) f_m=f_n *f_{m-(n+1)} +f_{n+1}*f_{m-n}\rightarrow \gcd(f_m,f_n)=\gcd(f_n,f_{n+1}*f_{m-n} )=\gcd(f_n,f_{m-n}) fm=fn∗fm−(n+1)+fn+1∗fm−n→gcd(fm,fn)=gcd(fn,fn+1∗fm−n)=gcd(fn,fm−n).这个可以更相减损,所以可以归纳出 gcd ( n , m ) \gcd(n,m) gcd(n,m),证毕!
int T,n,mod;
ll power(ll a,ll b=mod-2) {
ll c=1;
while(b) {
if(b&1) c=c*a%mod;
b /= 2; a=a*a%mod;
}
return c;
}
ll f[N];
int main() {
qr(T); while(T--) {
qr(n); qr(mod);
f[1]=1;
for(int i=2;i<=n;i++) f[i]=(2*f[i-1]+f[i-2])%mod;
for(int i=1;i*2<=n;i++) {
ll t=power(f[i]);
for(int j=i*2;j<=n;j+=i) f[j]=f[j]*t%mod;
}
ll ans=0,s=1;
for(int i=1;i<=n;i++) {
s=s*f[i]%mod;
ans += s*i%mod;
}
pr2(ans%mod);
}
return 0;
}
我们设 k t h m a x ( S ) = ∑ S ⊆ T f ( ∣ T ∣ ) min ( T ) kthmax(S)=\sum_{S\subseteq T} f(|T|) \min(T) kthmax(S)=∑S⊆Tf(∣T∣)min(T).
那么第 x x x 大数的贡献为 [ x = k ] = g ( x ) = ∑ i = 0 x − 1 f ( i + 1 ) ( x − 1 i ) [x=k]=g(x)=\sum_{i=0}^{x-1} f(i+1) \dbinom {x-1} i [x=k]=g(x)=∑i=0x−1f(i+1)(ix−1).
由二项式反演得: f ( x + 1 ) = ∑ i = 0 x ( − 1 ) x − i ( x i ) g ( i + 1 ) = ( − 1 ) x − ( k − 1 ) ( x k − 1 ) f(x+1)=\sum_{i=0}^x (-1)^{x-i} \dbinom x i g(i+1)=(-1)^{x-(k-1)}\dbinom x {k-1} f(x+1)=∑i=0x(−1)x−i(ix)g(i+1)=(−1)x−(k−1)(k−1x) ,即 f ( x ) = ∑ i = 0 x ( − 1 ) x − k ( x − 1 k − 1 ) f(x)=\sum_{i=0}^x (-1)^{x-k} \dbinom {x-1}{k-1} f(x)=∑i=0x(−1)x−k(k−1x−1).
例题
列出公式 k t h max ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − k ( ∣ T ∣ − 1 k − 1 ) min ( T ) , min ( T ) = 1 ∑ p kth\max(S)=\sum_{T\subseteq S} (-1)^{|T|-k}\dbinom {|T|-1}{k-1} \min(T),\min(T) =\dfrac 1{\sum p} kthmax(S)=∑T⊆S(−1)∣T∣−k(k−1∣T∣−1)min(T),min(T)=∑p1.
最暴力的做法为 O ( 2 n ) O(2^n) O(2n).
如果我们像背包一样记录 ∣ T ∣ , ∑ p |T|,\sum p ∣T∣,∑p总复杂度为 O ( n 2 m ) O(n^2 m) O(n2m).这样还是会T.
我们发现 k ≤ 11 k\le 11 k≤11,那么我们可以考虑试着把 k k k放进dp的状态内.
定义$f_{i,j,k} 表 示 钦 定 表示钦定 表示钦定k , , ,j=\sum p 时 , 时, 时,\sum (-1)^{|T|-1} \dbinom {|T|-k}{k-1}$ 的大小.
若第 i i i 个位置不选有 f i , j , k = f i − 1 , j , k f_{i,j,k}=f_{i-1,j,k} fi,j,k=fi−1,j,k.
选择的话,有 f i , j , k = ∑ ( − 1 ) ∣ T ∣ − k ( ∣ T ∣ − 1 k − 1 ) = ∑ ( − 1 ) ∣ T ∣ − k + 1 ( ∣ T ∣ k − 1 ) ( ∣ T ∣ 表 示 前 一 个 状 态 的 大 小 ) = ∑ ( − 1 ) ∣ T ∣ − k + 1 [ ( ∣ T ∣ − 1 k − 1 ) + ( ∣ T ∣ − 1 k − 2 ) ] = ∑ ( − 1 ) ∣ T ∣ − k + 1 ( ∣ T ∣ − 1 k − 1 ) + ( − 1 ) ∣ T ∣ − k + 1 ( ∣ T ∣ − 1 k − 2 ) = f i − 1 , j − p [ i ] , k − 1 − f i − 1 , j − p [ i ] , k f_{i,j,k}=\sum (-1)^{|T|-k} \dbinom {|T|-1}{k-1}=\sum (-1)^{|T|-k+1}\dbinom{|T|}{k-1}(|T|表示前一个状态的大小)=\sum (-1)^{|T|-k+1}\left[ \dbinom {|T|-1}{k-1}+\dbinom{|T|-1}{k-2}\right]=\sum (-1)^{|T|-k+1} \dbinom {|T|-1}{k-1}+(-1)^{|T|-k+1} \dbinom{|T|-1}{k-2}=f_{i-1,j-p[i],k-1}-f_{i-1,j-p[i],k} fi,j,k=∑(−1)∣T∣−k(k−1∣T∣−1)=∑(−1)∣T∣−k+1(k−1∣T∣)(∣T∣表示前一个状态的大小)=∑(−1)∣T∣−k+1[(k−1∣T∣−1)+(k−2∣T∣−1)]=∑(−1)∣T∣−k+1(k−1∣T∣−1)+(−1)∣T∣−k+1(k−2∣T∣−1)=fi−1,j−p[i],k−1−fi−1,j−p[i],k.
特别的,边界设定 f x , 0 , 0 = 1 f_{x,0,0}=1 fx,0,0=1,这个是为了保证 k = 1 k=1 k=1时候的正确性~~(其余情况 j > 0 , k = 0 j>0,k=0 j>0,k=0显然为0)
需要注意的是,直接三维会爆空间,需要滚掉 i i i维.
#include
using namespace std;
const int N=1010,K=12,M=10010,mod=998244353;
typedef long long ll;
int n,k,m,p[N];
ll f[2][M][K],inv[M];
int main(){
scanf("%d%d%d",&n,&k,&m); k=n-k+1;
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
int x=0,y=1; f[x][0][0]=1;
for(int i=1;i<=n;i++) {
swap(x,y); memset(f[x],0,sizeof f[x]),f[x][0][0]=1;
for(int j=1;j<p[i];j++)
for(int v=1;v<=k;v++)
f[x][j][v]=f[y][j][v];
for(int j=p[i];j<=m;j++)
for(int v=1;v<=k;v++)
f[x][j][v]=(f[y][j][v]+f[y][j-p[i]][v-1]-f[y][j-p[i]][v]+mod)%mod;
}
ll ans=0;
inv[1]=1; for(int i=2;i<=m;i++) inv[i]=inv[mod%i]*(mod-mod/i)%mod;
for(int i=1;i<=m;i++) ans += f[x][i][k]*inv[i]%mod;
printf("%lld\n",ans%mod*m%mod); return 0;
}