组队练习数学-04
Posted Lnn.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了组队练习数学-04相关的知识,希望对你有一定的参考价值。
传送门
CF466D
CF1666F
CF1228E
CF932E
CF666C
A. cf466D
题意:长n的数组a,每次选定区间[l,r],使得区间内的数字都加1,但是每个点作为l,r最多各一次,问最小多少次使得数组全变成h
Solution from lnn:
1.可以发现相邻两个数字最多差1
2.定义dp[i]为前i个数符合条件的方案数,每个点需要被覆盖的次数:las = h-a[i-1] , now = h-a[i]
当las+1 == now时,延续之前的线段并在i点新启一个线段,dp[i] = dp[i-1]
当las-1 == now时,可以选一个区间停在i-1,dp[i] = dp[i-1]*las
当las == now时,可以直接延续,也可以停一个再新启,dp[i] = dp[i-1]*(las + 1)
3.时间复杂度:O(N)
void solve()
for(ll i = 1 ; i <= n ; ++i)
if(a[i] > h)
cout << 0 << endl ;
return;
if(a[n]+1 < h)
cout << 0 << endl ;
return;
a[0] = h;
dp[0] = 1;
for(ll i = 1 ; i <= n ; ++i)
ll las = h - a[i-1];
ll now = h - a[i];
if( !(las-1 <= now && now <= las+1) )
cout << 0 << endl ;
return;
if(now == las)
dp[i] = (dp[i-1]*(1 + las))%mod;
else if(now < las)
dp[i] = dp[i-1]*las%mod;
else if(now > las)
dp[i] = dp[i-1];
cout << dp[n] << endl ;
B.cf1666F
题意:给定一组数字a,n<=5e3,重新排列,满足以下两个条件,求方案数
1.b1b3…>bn−1<bn (偶数位置作为波峰
2.b2<b4<b6<…<bn (偶数位置递增
Solution from lnn:
1.可以发现最大的两个数字只能放在最后两个偶数位置,顺着想,发现应该按照a从大到小考虑,
那么数字只能从后往前放置,偶数只能一个一个贴着放,奇数可以放在已经放置的偶数之间
2.dp[i][j]表示>=i的数全都放置完毕了,并且放置了j个偶数
cnt[i]表示数字i出现了几次,suf[i]为cnt的后缀
如果i-1全放置在奇数位置,设剩余res个奇数位置
dp[i-1][j] = dp[i][j] * res * (res-1) * (res-2) * … * (res - cnt[i-1]+1)
如果i-1放置在偶数位置,偶数递增,最多放置一个
dp[i-1][j+1] = cnt[i-1] * dp[i][j] * res * … * (res - cnt[i-1]+2)
奇数放置是有重复的,最后的答案dp[1][n/2]要除以所有的cnt[i]!
所以偶数的转移多乘一个cnt[i-1],统一去重
3.时间复杂度:O(N^2)
void solve()
dp[tot+1][0] = 1;
for(ll i = tot+1 ; i >= 2 ; --i) /// >= i 的数已经处理完毕
for(ll j = 0 ; j <= nnn/2 ; ++j) /// 已经占用j个偶数
ll odd = (j == nnn/2 ? nnn/2 : max(0ll , j-1) );
odd -= (suf[i] - j);
///全放在奇数位置
if(a[i-1] <= odd)
ll jie = fac[odd] * inv[odd-a[i-1]]%mod;
dp[i-1][j] = (dp[i-1][j] + dp[i][j] * jie%mod )%mod;
if(j < nnn/2 && a[i-1]-1 <= odd)
ll jie = fac[odd] * inv[odd-a[i-1]+1]%mod;
dp[i-1][j+1] = (dp[i-1][j+1] + a[i-1] * dp[i][j]%mod * jie%mod)%mod;
ll ans = dp[1][nnn/2];
for(ll i = 1 ; i <= tot ; ++i)
ans = ans * inv[a[i]]%mod;
cout << (ans%mod + mod)%mod << endl ;
C. cf1228E
题意:一个n*n的方阵,每个位置可以填1~k,求多少种填法,使得每行每列至少有1一个位置有1.(n<=250,k<=1e9)
Solution from y:容斥
法一:
设f(i)表示至多i行存在1,且每列都有1。
设g(i)表示恰好i行存在1,且每列有1.
则 a n s = g ( n ) = ∑ i = 0 n ( − 1 ) n − i C ( n , i ) f ( i ) ans = g(n) =\\sum_i=0^n (-1)^n-iC(n,i)f(i) ans=g(n)=∑i=0n(−1)n−iC(n,i)f(i)
而 f ( i ) = ( k i − ( k − 1 ) i ) n ∗ ( k − 1 ) n 2 − n i f(i) = (k^i-(k-1)^i)^n * (k-1)^n^2-ni f(i)=(ki−(k−1)i)n∗(k−1)n2−ni,上面说至多i行存在1,即剩下n-i行一定没有1,方案数为 ( k − 1 ) n 2 − n i (k-1)^n^2-ni (k−1)n2−ni,选定的i行可能存在1,方案数为 ( k i − ( k − 1 ) i ) n (k^i-(k-1)^i)^n (ki−(k−1)i)n,表示用1k的数任意填的方案-用2k的数任意填的方案数。
因为算至多的时候可以将每列都有1的方案算出来,所以容斥1次即可。
#define int long long
const int N=1e3+5;
const int mod=1e9+7;
const double eps=1e-8;
int n,m,k;
int fac[N],ni_f[N];
ll qsm(int a,int b)
ll ans=1,tmp=a;
while( b )
if( b&1 ) ans = ans * tmp%mod;
tmp = tmp * tmp%mod;
b>>=1;
return ans;
void ini()
int maxn = 1e3;
fac[0]=1;
_for(i,1,maxn) fac[i] = (fac[i-1] * i)%mod;
ni_f[maxn] = qsm(fac[maxn],mod-2);
_rep(i,maxn-1,0) ni_f[i] = ni_f[i+1] * (i+1)%mod;
ll C(int n,int m)
if( m==n || m==0 ) return 1;
if( n < m ) return 0;
return fac[n] * ni_f[n-m]%mod * ni_f[m]%mod;
signed main()
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios;
ini();
cin>>n>>k;
int ans=0;
_for(i,1,n)
int flag = (n-i)&1?-1:1;
int t = (qsm(k,i) - qsm(k-1,i) + mod )%mod;
int yu = n*n - n*i;
ans = (ans + mod + flag * C(n,i) * qsm(t,n)%mod * qsm(k-1,yu)%mod )%mod;
cout<<ans<<endl;
AC;
法二:
考虑求不合法的方案。
设f(i)表示钦定i行没有1,列都有1的方案数。
g(i)表示恰好i行没有1,列都有1的方案数。
则 a n s = g ( 0 ) = ∑ i = 0 n ( − 1 ) i C ( n , i ) f ( i ) ans =g(0)= \\sum_i=0^n(-1)^iC(n,i)f(i) ans=g(0)=∑i=0n(−1)iC(n,i)f(i)
再去求f(i),再用一次容斥
设h(i,j)表示在i行没有1的情况下,钦定j行没有1
则$ f(i) = \\sum_j=0n(-1)jC(n,j)h(i,j)$
而 h ( i , j ) = ( k − 1 ) n i ∗ ( k − 1 ) j ( n − i ) ∗ k n 2 − n i − j ( n − i ) h(i,j) = (k-1)^ni*(k-1)^j(n-i)*k^n^2-ni-j(n-i) h(i,j)=(k−1)ni∗(k−1)j(n−i)∗kn2−ni−j(n−i)
因为求的是不合法情况,即行和列都要求反面,需要容斥两次。
D . cf932E
题意:求 ∑ i = 1 n i k C ( n , i ) , 其中 n < = 1 e 9 , k < = 5000 \\sum_i=1^ni^kC(n,i),其中n<=1e9,k<=5000 ∑i=1nikC(n,i),其中n<=1e9,k<=5000
Solution from y:第二类斯特林数
注意到k的范围比较小,考虑枚举k。
从组合数学的意义 i k i^k ik表示i个有区别的盒子,k个不同球放置到盒子的方案数。
最多有k个盒子不是空盒子,考虑枚举非空盒子。
枚举非空盒子,然后将k个球放置到盒子中,这里有第二类斯特林数,由于第二类斯特林数的盒子是无区别的所以还要乘上阶乘,代表给盒子编号。
∑
i
=
1
n
i
k
C
(
n
,
i
)
=
∑
i
=
1
n
C
(
n
,
i
)
∑
j
=
0
k
S
2
(
k
,
j
)
j
!
C
(
i
,
j
)
\\sum_i=1^ni^kC(n,i)=\\sum_i=1^nC(n,i)\\sum_j=0^kS2(k,j)j!C(i,j)
∑i=1nikC(n,i)=∑i=1nC(< 以上是关于组队练习数学-04的主要内容,如果未能解决你的问题,请参考以下文章