组队练习数学-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)niC(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(k1)i)n(k1)n2ni,上面说至多i行存在1,即剩下n-i行一定没有1,方案数为 ( k − 1 ) n 2 − n i (k-1)^n^2-ni (k1)n2ni,选定的i行可能存在1,方案数为 ( k i − ( k − 1 ) i ) n (k^i-(k-1)^i)^n (ki(k1)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)=(k1)ni(k1)j(ni)kn2nij(ni)

因为求的是不合法情况,即行和列都要求反面,需要容斥两次。

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的主要内容,如果未能解决你的问题,请参考以下文章

组队练习数学-04

请问数学符号SIGMA(∑)有啥运算法则么?

数论与数学专题练习(201802~201805)

算法练习84.找出数组的最大公约数——数学

Python练习题 003:完全平方数

SCOI2007 组队