20241214北京总结

总结

做题 : \(CEQ\)

授课内容 : 线性代数 生成函数 容斥原理 斯特林数

未理解 : 全部

练习题总结 : 如下 , 自习写的 , 今天主要是现补了一下什么是生成函数

AT_agc016_c [AGC016C] +/- Rectangle

手玩一下两个是 \(Yes\) 的样例 , 有一个朴素的思路 , 就是其他位置全填 \(1\) , 直到这里如果再填 \(1\) 就在 \(h*w\) 的范围为正了 , 那么就强制填一个负数让他变负

所以随着做随着搞一个前缀和就可以了 , 发现没过 , 搓一组反例 1 3 1 2

发现按照我的构造它会变成 1 -2 1, 但是可以做到 2 -3 2 , \(so\)我唐完了

所以把填的数变成一个比较大的还不会导致填出绝对值大于 \(10^9\) 就行,选的 \(500\)

/*
 * @Author: 2019yyy
 * @Date: 2024-12-14 08:36:36
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-14 09:04:58
 * @FilePath: \code\20241214\agc106C.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
int a[510][510],sum[510][510];
int main(){
    int n,m,h,w;
    cin>>n>>m>>h>>w;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
            if(i>=h and j>=w){
                int temp=sum[i][j]-sum[i-h][j]-sum[i][j-w]+sum[i-h][j-w];
                //cout<<i<<" "<<j<<" "<<temp<<'\n';
                if(temp<0){
                    a[i][j]=500;
                }else{
                    a[i][j]=-temp-1;
                }
            }else{
                a[i][j]=500;
            }
            sum[i][j]+=a[i][j];
        }
    }
    if(sum[n][m]<=0){
        cout<<"No\n";
        return 0;
    }
    cout<<"Yes\n";
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cout<<a[i][j]<<" ";
        }
        cout<<'\n';
    }
    return 0;
}

生成函数

以数列的第 \(n\) 项作为 \(x^n\) 项的系数 , 形式化的: \(F(x)=\displaystyle\sum_na_nx^n\)

\(a= \left \langle 1,0,1,0..... \right \rangle\) , 那么 \(F(x)=\displaystyle\sum_nx^{2n}\)

为了化简 , 我们通常会把它变成一个封闭形式

\(a= \left \langle 1,0,1,0..... \right \rangle\) , 那么 \(x^2F(x)+1=F(x)\) , 即\(F(x)=\frac{1}{1-x^2}\)

下面给一些常用的 ( 我遇到的 ):

\(F(x)=\displaystyle\sum_nx^{kn}=\frac{1}{1-x^k}\)

\(F(x)=\displaystyle\sum_np^nx^{n}=\frac{1}{1-nx}\) ( 等比数列 )

\(F(x)=\displaystyle\sum_n\binom{m}{n}x^{n}=(1+x)^m\)

在接下来我们需要一些前置知识

广义组合数 广义二项式定理

我们需要有一个前置知识 : 广义二项式定理

我们先来看看广义组合数 , 即:

\[\binom{n}{m}=\frac{\prod^m_{i=m-n+1}i}{n!} \]

\(so\) :

\(n > m >0\)时 , 有 :

\[\binom{-m}{n}=\frac{\prod^{-m}_{i=-m-n+1}i}{n!}=\binom{n+m-1}{m-1}\times(-1)^n \]

接着广义二项式定理 :

对于任意一个\(k\) , 都有 :

\[(1+x)^k=\sum^{+\infty}_{i=0} \binom{k}{i}\times x^i \]

\[(1-x)^k=\sum^{+\infty}_{i=0} \binom{k+i-1}{k-1}\times x^i \]

所以 , 我们可以求出\(a_n=\binom{m}{n}\) 的生成函数 , 即为

\[F(x)=\sum^{+\infty}_{i=0}\binom{m}{i}\times x^i=(1+x)^m \]

P10780 BZOJ3028 食物

对每一种食物推生成函数

  • 承德汉堡 : \(F(x)=\displaystyle\sum_nx^{2n}=\frac{1}{1-x^2}\)
  • 可乐 : \(F(x)=x+1\)
  • 鸡腿 : \(F(x)=x^2+x+1=\displaystyle\frac{x^3-1}{x-1}\)
  • 蜜桃多 : \(F(x)=\displaystyle\sum_nx^{2n+1}=\frac{x}{1-x^2}\)
  • 鸡块 : \(F(x)=\displaystyle\sum_nx^{4n}=\frac{1}{1-x^4}\)
  • 包子 : \(F(x)=x^3+x^2+x+1=(x+1)(x^2+1)\)
  • 土豆片炒肉 : \(F(x)=x+1\)
  • 面包:\(3\) 的倍数个 : \(F(x)=\displaystyle\sum_nx^{3n}=\frac{1}{1-x^3}\)

把它们的生成函数全乘到一起 , 发现等于\(\displaystyle\frac{x}{(x-1)^4}\)

之后把这个封闭形式摊开

\[\begin{align*} \frac{x}{(x-1)^4} &= x(x-1)^{-4} \\ &= x\sum_{k=0}^{+\infty} \binom{-4}{k}x^k\times(-1)^{-4-k}\ \\ &= x\sum_{k=0}^{+\infty} \binom{k+3}{3}x^k\times(-1)^k\times(-1)^{-4-k}\\ &= x\sum_{k=0}^{+\infty} \binom{k+3}{3}x^k\\ &= \sum_{k=1}^{+\infty} \binom{k+2}{3}x^k \end{align*} \]

最后答案就是\(\binom{k+2}{3}=\frac{(k+2)\times\ (k+1)\times k}{6}\) , 最后一边读入一边\(mod\) ( 但我用\(py\)水过了 )

mod=10007
n=int(input())
n=n%mod
ans=n*(n+1)%mod*(n+2)%mod*1668%mod
print(int(ans))

还有一道类似的P2000拯救世界 , 不再过多赘述

[ABC241Ex] Card Deck Score

\[\sum_{c_1+c_2+\dots+c_n=m}\prod^n_{i=1} a_i^{c_i} \]

考虑生成函数

\[F(x)=\sum^{b_i}_{j=0}a_i^jx^j \\ F(x)\times a_ix-a_i^{b_i+1}x^{b_i+1}+1=F(x)\\ F(x)=\frac{a_i^{b_i+1}x^{b_i+1}-1}{a_ix-1}\]

分子可以在\(O(2^n)\)求出 , 分母单独考虑

\(G(x)=\prod^n_{i=1}(a_ix-1)^{-1}=\sum^n_{i=1}t_i(a_ix-1)^{-1}\)

把右式通分同乘分母得\(\sum^n_{i=1}(t_i\prod_{j\neq i}a_ix-1)=1\)

我们可以任取 \(16\)\(x\) , 高斯消元

求完 \(t\) 以后卷一下

/*
 * @Author: 2019yyy
 * @Date: 2024-12-14 16:23:16
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-14 16:56:30
 * @FilePath: \code\20241214\abc241Ex.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int a[11000000],b[1100000],f[1100000];
int v[(1<<16)],w[(1<<16)];
int qpow(int x,int y){
    int res=1;
    while(y){
        if(y&1){
            res=res*x%mod;
        }
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
        b[i]++;
    }
    for(int i=0;i<=(1<<n)-1;i++){
        v[i]=1;
        for(int j=0;j<=n-1;j++){
            if((i>>j)&1){
                v[i]=v[i]*((mod-qpow(a[j+1],b[j+1])%mod)%mod)%mod;
                w[i]=w[i]+b[j+1];
            }
        }
    }
    for(int i=1;i<=n;i++){
        int inv=qpow(a[i],mod-2);
        f[i]=1;
        for(int j=1;j<=n;j++){
            if(j==i){
                continue;
            }
            f[i]=f[i]*((1+mod-a[j]*inv%mod)%mod)%mod;
        }
        f[i]=qpow(f[i],mod-2);
    }
    int ans=0;
    for(int i=0;i<=(1<<n)-1;i++){
        if(w[i]>m){
            continue;
        }
        for(int j=0;j<=n-1;j++){
            ans=ans+v[i]*f[j+1]%mod*qpow(a[j+1],m-w[i]);
            ans%=mod;
        }
    }
    cout<<ans<<'\n';
}

容斥

简单来说 , 思想很简单 , 把有用的贡献算为 \(1\) 或一个常数 , 把没有用的贡献算为 \(0\)

P3266 [JLOI2015] 骗我呢

把这个图拉伸一下 , 变成了切掉两个角的网格图 , 进而变成不过两条直线的网格图

首先考虑只有一条线我们应该怎么做 , 这是一个类似卡特兰数的问题 , 就把终点关于直线对称一下 , 再剪掉到新终点的方案数就行

我们多了一条线这种做法就不太可做了 , 因为可以同时过两条线 , 我们继续应用刚才的容斥思想

即标号过两条线分别为 \(AB\) , 过 \(A\) - 过 \(AB\) + 过 \(ABA\) -.....

那么先过 \(A\) 再过 \(B\) 的要怎么做 , 只要把之前对称出来的点再关于 \(B\) 对称一下就行

/*
 * @Author: 2019yyy
 * @Date: 2024-12-14 19:25:47
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-14 22:06:23
 * @FilePath: \code\20241214\P3266.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int fac[11000000];
int qpow(int x,int y){
    int res=1;
    while(y){
        if(y&1){
            res=res*x%mod;
        }
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
int inv(int x){
    return qpow(x,mod-2);
}
int C(int x,int y){
    if(x<0){
        return 0;
    }
    if(x>y){
        return 0;
    }
    return fac[y]*inv(fac[x])%mod*inv(fac[y-x])%mod;
}
int n,m;
void change(int &x,int &y,int val){
    swap(x,y);
    x-=val;
    y+=val;
}
signed main(){
    cin>>n>>m;
    fac[0]=1;
    for(int i=1;i<=3e6+1;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    int x=n+m+1,y=n;
    int ans=C(y,x+y);
    while(x>=0 and y>=0){
        change(x,y,1);
        ans+=mod-C(x,x+y);
        ans%=mod;
        change(x,y,-(m+2));
        ans+=C(x,x+y);
        ans%=mod;
    }
    x=n+m+1,y=n;
    while(x>=0 and y>=0){
        change(x,y,-(m+2));
        ans+=mod-C(x,x+y);
        ans%=mod;
        change(x,y,1);
        ans+=C(x,x+y);
        ans%=mod;
    }
    cout<<ans%mod<<'\n';
    return 0;
}
posted @ 2024-12-14 22:17  2019yyy  阅读(24)  评论(0)    收藏  举报