省选武汉联测 7

不要停止演奏,就算你的身体已如碎肉一般亦是如此。

线性代数在 oi 中的应用 重庆市巴蜀中学 郭雨豪

摘要:线性代数在 oi 中没有用。

动点(point)

首先考虑不带修。这显然,线段树维护矩阵乘。

然后把修改加上。修改看起来每个矩阵都要乘一下,没法修改。但是我们不考虑矩阵,直接改坐标系。

对于平移,把点先逆向平移,做完操作之后再正向平移回来。对称同理,但是对称之后旋转方向要变一下。旋转维护一个逆时针一个顺时针矩阵就行了。

没写。

有限域方阵期望对数转置相关度(transpose)

线性代数。sb。

关于题目背景:我们都知道怎么求特征多项式。不知道的右转洛谷模板题解。

首先满足 \(AB=A^T\) 的矩阵 \(A\) 需要满足行向量集和列向量集相等(这是结论)。那我们枚举这些向量 \(C\) ,那么设 \(A\) 的秩为 \(r\)\(A\) 就可以表示为 \(A=C^TMC\),其中 \(C\)\(n\times r\) 满秩矩阵, \(M\)\(r\times r\) 满秩矩阵。它的方案数显然是

\[\begin{bmatrix}n\\r\end{bmatrix}_p\prod_{i=0}^{r-1}p^r-p^i=\begin{bmatrix}n\\r\end{bmatrix}_p(p^r)^r\prod_{i=1}^r1-p^{-i} \]

然后考虑 \(R_{\log}(A)\) 是多少。对于矩阵 \(A\),我们计算 \(B\) 的个数。首先一组向量的方案数是 \(p^n\),有 \(n-r\) 组可以随意定,那么 \(R(A)=p^{n(n-r)}\),即得到 \(R_{\log}(A)=n(n-r)+1\)。那这两个卷积一下就行了。

sb 任意模数。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const long double pi=acos(-1);
const int sq=(1<<15)-1;
struct cp{
    long double r,i;
    cp(long double a=0,long double b=0){r=a;i=b;}
    cp operator+(const cp &s)const{return cp{r+s.r,i+s.i};}
    cp operator-(const cp &s)const{return cp{r-s.r,i-s.i};}
    cp operator*(const cp &s)const{return cp{r*s.r-i*s.i,r*s.i+i*s.r};}
}a[300010],b[300010],p[300010],q[300010];
int n,m,P,mod,wl=1,r[300010],ans[300010],a1[300010],b1[300010],f[300010],last[300010];
void get(int n){
    while(n>=wl)wl<<=1;
    for(int i=0;i<=wl;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(__lg(wl)-1));
}
void fft(cp a[],int n,int tp){
    for(int i=1;i<n;i++)if(i<r[i])swap(a[i],a[r[i]]);
    for(int mid=1;mid<n;mid<<=1){
        cp wn={cos(pi/mid),tp*sin(pi/mid)};
        for(int j=0;j<n;j+=mid<<1){
            cp w={1,0};
            for(int k=0;k<mid;k++,w=w*wn){
                cp x=a[j+k],y=w*a[j+mid+k];
                a[j+k]=x+y;a[j+mid+k]=x-y;
            }
        }
    }
    if(tp^1)for(int i=0;i<n;i++)a[i].r/=n,a[i].i/=n;
}
void mul(int n,int a1[],int b1[],int c[]){
	for(int i=0;i<=n;i++)a[i]=cp (a1[i]&sq,a1[i]>>15);
	for(int i=0;i<=n;i++)b[i]=cp (b1[i]&sq,b1[i]>>15);
	fft(a,wl,1);fft(b,wl,1);
	for(int i=0;i<wl;i++){
        int ret=(wl-i)&(wl-1);
        p[i]=(cp){0.5*(a[i].r+a[ret].r),0.5*(a[i].i-a[ret].i)}*b[i];
        q[i]=(cp){0.5*(a[i].i+a[ret].i),0.5*(a[ret].r-a[i].r)}*b[i];
    }
    fft(p,wl,-1);fft(q,wl,-1);
    for(int i=0;i<wl;i++){
        long long p1=p[i].r+0.5,q1=p[i].i+0.5,x=q[i].r+0.5,y=q[i].i+0.5;
        c[i]=(p1%mod+((q1+x)%mod<<15)+((y%mod)<<30))%mod;
    }
}
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
int jc[300010],inv[300010];
int main(){
    scanf("%d%d%d",&n,&P,&mod);
    jc[0]=inv[0]=1;
    int ret=1;
    for(int i=1;i<=n;i++){
        ret=1ll*ret*P%mod;
        jc[i]=1ll*jc[i-1]*(1-ret+mod)%mod;
        inv[i]=qpow(jc[i],mod-2);
    }
    int Iv=qpow(P,mod-2);ret=1;f[0]=1;
    for(int i=1;i<=n;i++){
        ret=1ll*ret*Iv%mod;
        f[i]=1ll*f[i-1]*(1-ret+mod)%mod;
    }
    for(int i=1;i<=n;i++)f[i]=1ll*f[i]*qpow(P,1ll*i*i%(mod-1))%mod*inv[i]%mod;
    for(int i=0;i<=n;i++)a1[i]=f[i];
    for(int j=0;j<=n;j++)b1[j]=1ll*inv[j]*j%mod;
    get(n+1<<1);
    mul(wl,a1,b1,ans);
    for(int i=1;i<=n;i++)last[i]=1ll*ans[i]*i%mod;
    memset(a1,0,sizeof(a1));memset(b1,0,sizeof(b1));memset(ans,0,sizeof(ans));
    memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(p,0,sizeof(p));memset(q,0,sizeof(q));
    for(int i=0;i<=n;i++)a1[i]=f[i];
    for(int j=0;j<=n;j++)b1[j]=inv[j];
    mul(wl,a1,b1,ans);
    for(int i=1;i<=n;i++){
        last[i]=1ll*(last[i]+ans[i])%mod*jc[i]%mod;
        printf("%d\n",last[i]);
    }
    return 0;
}

灭国(destroy)

贪心。

首先不加边的话就是把拓扑排序的队列变成小根堆。考虑加边怎么做。

首先我们肯定是越大越好,那么拓扑排序的时候把当前所有能到达的点找个最大的作为当前点,剩下的放到另一个堆。如果当前的堆为空,则把另一个堆里的最大值扔过来,并给他从当前拓扑序最后的点连一条边。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
int n,m,k,d[100010];
set<int>s,s0;
map<int,int>mp[100010];
vector<int>g[100010];
int cnt,ans[100010];
struct stu{
    int u,v;
}eg[100010];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        g[u].push_back(v);d[v]++;mp[u][v]=1;
    }
    for(int i=1;i<=n;i++)sort(g[i].begin(),g[i].end());
    for(int i=1;i<=n;i++)if(!d[i])s.insert(i);
    while(k&&!s.empty()){
        s0.insert(*s.begin());s.erase(s.begin());
        k--;
    }
    int pos=0;
    for(int i=1;i<=n;i++){
        if(s.empty()){
            int x=*prev(s0.end());
            s0.erase(x);s.insert(x);
            if(!pos||mp[pos].find(x)!=mp[pos].end())k++;
            else eg[++cnt]={pos,x};
        }
        int x=*s.begin();s.erase(x);
        ans[++ans[0]]=x;
        for(int v:g[x]){
            d[v]--;
            if(!d[v]){
                if(k)s0.insert(v),k--;
                else s.insert(v);
            }
        }
        pos=x;
    }
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    printf("\n%d\n",cnt);
    for(int i=1;i<=cnt;i++)printf("%d %d\n",eg[i].u,eg[i].v);
    return 0;
}

然而比起题目来说感觉更值得一说的是题目背景。

请证明:对于任意正项序列 \(\{a_n\}\),有

\[\varlimsup_{n\to\infty}\left(\frac{1+a^{n+1}}{a_n}\right)^n\ge e \]

并且这个估计是最好的。

这是卓里奇《数学分析》第三章第二节的习题 11。

看起来这个和 \(\lim_{n\to\infty}(1+\dfrac 1n)^n=e\) 长的差不太多,于是右边代替。同时左边是上极限,于是只要证明有子列满足。即可化简两侧:

\[\left(\frac{1+a_{n+1}}{a_n}\right)^n\ge\lim_{n\to\infty}(1+\dfrac 1n)^n \]

\[n\left(\frac{1+a_{n+1}}{a_n}-1\right)\ge 1 \]

反证。如果不成立,则恒有

\[n\left(\frac{1+a_{n+1}}{a_n}-1\right)<1 \]

\[\frac 1{n+1}+\frac{a_{n+1}}{n+1}<\frac{a_n}n \]

从而得到

\[\sum_{i=1}^n\frac 1i+\frac{a_n}n<a_1+1 \]

\(n\to\infty\)

\[\sum_{n=1}\frac 1n+\varlimsup_{n\to\infty}\frac{a_n}n\le a_1+1 \]

显然是错的。于是得证。

posted @ 2023-03-19 19:58  gtm1514  阅读(46)  评论(0编辑  收藏  举报