zoukankan      html  css  js  c++  java
  • 【考试总结】20220103

    young

    推广 \(m\le 4\) 的部分分发现求最小生成树的过程就是从高到低考察每一位,对于点权在这位为 \(0\) 的点会被连成一个连通块而点权为 \(0\) 的点连成一个连通块,最后使用一条边将这两个连通块连起来,同时连接的两个点是两个连通块里面 \(\rm xor\) 值最小的一对

    \(f(n,m)\) 表示 \(n\) 个点,每个点点权在 \([0,2^m)\) 的生成树的边权和,那么执行上面的叙述进行计数:枚举这位点权是 \(0\) 的集合大小 \(s\),另 \(t=n-s\) 即第 \(m\) 位点权为 \(1\) 的集合大小

    此时最小生成树边权和被拆成了 两个点集内部 和 两个点集之间 的两个部分,点集内部的求解便是一个子问题,会给 \(f(n,m)\) 贡献 \(f(t,m-1)\times 2^{(m-1)\times s}+f(s,m-1)\times 2^{(m-1)\times t}\)

    待解决的问题是两个集合之间的边权和,设 \(g(s,t,m)\) 表示两个大小为 \(s,t\) 集合,权值为 \([0,2^m)\) 的集合间连边权值和

    使用经典转化:\(n=\sum_{i=1}^n[n\ge i]\) 也就是说;另外计算 \(p(s,t,m,k)\) 表示同上述含义的局面中最终最小边的权值 \(\ge k\)方案数

    统计的方式和 \(f\) 类似,枚举 \(s\) 中在第 \(m\) 位为 \(0/1\) 的点数 \(s_{0/1}\)\(t\) 中第 \(m\) 位为 \(0/1\) 的点数 \(t_{0/1}\)

    \(k\) 的第 \(m\) 位为 \(1\) 时要求不能让 \(s_0,t_0\) 或者 \(s_1,t_1\) 同时出现,这等价于直接求 \(p(s,t,m-1,k-2^{m-1})\) 这个子问题,由于 \(s_0,t_1\)\(s_1,t_0\) 也是等价的,所以甚至可以直接乘 \(2\) 记为 \(p(s,t,m,k)\)

    \(k\) 的第 \(m\) 位为 \(0\) 时就要实打实的枚举大小了,这里与 \(f\) 求解处不同的是要求 \(s_0,t_0\)\(s_1,t_1\) 之间每个点的连边都要 \(\ge k\) 所以要递归两侧,分别是 \(p(s_0,t_0,m-1,k),g(s_1,t_1,m-1,k)\)

    注意 \(f,p\) 求解的时候显然需要乘组合数,边界除了 \(p\) 中是 \(m=0\) 判断 \(k\)\(0\) 和一个为 \(0\) 了剩下随便选 之外都是点集大小/位数不够了返回 \(0\)

    代码中的变量含义和叙述中的保持一致。同时为了不超过 80 符被 D 线,刻意增加了行数

    Code Display
    int pw[5010],n,m;
    int f[52][10],g[52][52][10],p[52][52][10][70],C[110][110];
    inline int P(int s,int t,int m,int k){
        if(s>t) swap(s,t);
        if(m==0) return k==0;
        if(s==0) return pw[m*t];
        if(~p[s][t][m][k]) return p[s][t][m][k]; p[s][t][m][k]=0;
        if(k>>(m-1)&1){
            p[s][t][m][k]=mul(2,P(s,t,m-1,k^(1<<(m-1))));
            return p[s][t][m][k];
        }    
        rep(i,0,s) rep(j,0,t){
            if(j==t&&i==0) continue;
            if(i==s&&j==0) continue;
            int addi=mul(C[s][i],C[t][j]);
            ckadd(p[s][t][m][k],mul(addi,mul(P(i,j,m-1,k),P(s-i,t-j,m-1,k))));
        }
        ckadd(p[s][t][m][k],pw[(s+t)*(m-1)+1]); // s==0 && t==0 
        return p[s][t][m][k];
    }
    inline int G(int s,int t,int m){
        if(s>t) swap(s,t);
        if(!s||!m) return 0;
        if(~g[s][t][m]) return g[s][t][m]; g[s][t][m]=0;
        for(int i=1;i<(1<<m);++i) ckadd(g[s][t][m],P(s,t,m,i));
        return g[s][t][m];
    }
    inline int F(int n,int m){
        if((n<=1)||!m) return 0; 
        if(~f[n][m]) return f[n][m];
        f[n][m]=0;
        for(int s=0;s<=n;++s){
            int v1=mul(F(s,m-1),pw[(n-s)*(m-1)]); // S self
            int v2=mul(pw[s*(m-1)],F(n-s,m-1)); // T self
            int v3=G(s,n-s,m-1);
            int v4=(s==0||s==n)?0:pw[(n+1)*(m-1)]; // between
            ckadd(f[n][m],mul(C[n][s],(v1+v2+v3+v4)%mod));
        }
        return f[n][m];
    }
    signed main(){
        pw[0]=1; for(int i=1;i<=5000;++i) pw[i]=add(pw[i-1],pw[i-1]);
        C[0][0]=1;
        for(int i=1;i<=100;++i){
            C[i][0]=1;
            for(int j=1;j<=i;++j) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
        }
        memset(f,-1,sizeof(f));
        memset(g,-1,sizeof(g));
        memset(p,-1,sizeof(p));
        n=read(); m=read(); 
        print(mul(ksm(pw[n*m],mod-2),F(n,m)));
        return 0;
    }
    

    simple

    对于一个长度为 \(n\) 的非周期串 \(S\)(即不存在 \(T\)\(|T|<|S|\)\(\rm len(T)\ |\ len(S)\)\(T\)\(S\) 的周期),其所有循环移位的最小者会被计入答案,也就是说任意一个长度为 \(len\) 非周期串对 \(f(len)\) 的贡献为 \(\frac 1{len}\)

    其实这就是最朴素的 \(\rm Lyndon\ Word\) 的结论,赛时没有推出的原因是一直在给 \(n\le 10^4\) 的档编 \(\rm DP\),甚至编了不少于两个小时也没能编出

    关于计算非周期串可以使用最平凡的莫比乌斯反演,即设 \(F(n)\) 表示长度为 \(n\) 的串的个数,\(G(n)\) 表示长度为 \(n\) 的非周期串的个数

    \[F(n)=\sum_{d|n}G(d)\Leftrightarrow G(n)=\sum_{d|n}\mu(\frac nd)F(d) \]

    将推导带回答案并推导,写出表达式如下:

    \[\begin{aligned}Ans&=\sum_{i=1}^ni^2\times \frac 1i\times G(i)\\ &=\sum_{i=1}^ni\sum_{d|i}\mu(\frac id)10^d\\ &=\sum_{d=1}^n d10^d\sum_{j=0}^{\lfloor\frac ni \rfloor}j\times\mu(j)\\ \end{aligned}\]

    最后一步使用了精彩的 \(i=j\times d\),那么后面部分的求解是杜教筛模板,其中 \(f=\mu\times id,g=id,h=\epsilon\)

    前面一部分是等差乘等比数列求和的计算,是一个历史遗留问题,我今天花了 \(30s\) 推导了一下得到了非常简洁的形式:

    \[\begin{aligned}S&=\sum_{i=0}^n i 10^i\\ 10S&=\sum_{i=1}^{n+1}(i-1)\times 10^i\\ -9S&=-n10^{n+1}+\sum_{i=1}^n 10^i\\ S&=\frac{n10^{n+1}-\frac{10^{n+1}-1}{9}}{9} \end{aligned}\]

    这就能做了,只需要会写快速幂!非常简单是不是!

    Code Display
    const int N=1e7+10,inv2=ksm(2,mod-2);
    int pre[N],n,ans,pri[N],mu[N],cnt;
    map<int,int> mp;
    bool fl[N];
    inline int S(int n){
        if(n<=1e7) return pre[n];
        if(mp.count(n)) return mp[n];
        int res=1;
        for(int l=2,r;l<=n;l=r+1){
            r=n/(n/l);
            res-=(r-l+1)%mod*((l+r)%mod)%mod*inv2%mod*S(n/l)%mod;
        }
        return mp[n]=(res%mod+mod)%mod;
    }
    const int inv9=ksm(9,mod-2);
    inline int F(int n){
        int kk=ksm(10,n+1);
        int r1=mul(n%mod,kk),r2=mul(del(kk,1),inv9);
        return mul(del(r1,r2),inv9);
    }
    signed main(){
        n=1e7; mu[1]=1;
        for(int i=2;i<=n;++i){
            if(!fl[i]) mu[i]=-1,pri[++cnt]=i;
            for(int j=1;i*pri[j]<=n&&j<=cnt;++j){
                fl[i*pri[j]]=1;
                if(i%pri[j]==0){
                    mu[i*pri[j]]=0;
                    break;
                }else{
                    mu[i*pri[j]]=-mu[i];
                }
            }
        }
        for(int i=1;i<=n;++i){
            pre[i]=i*mu[i]+pre[i-1];
            pre[i]=(pre[i]%mod+mod)%mod;
        }
        n=read();
        for(int l=1,r;l<=n;l=r+1){
            r=n/(n/l);
            ans+=mul(del(F(r),F(l-1)),S(n/l));
        }
        print(ans%mod);
        return 0;
    }
    

    meet

    使用最基础的 \(\rm exLucas\) 即可解决,那么写这么长长的一篇博客就是来写一份 \(\rm exLucas\) 学习笔记的!

    记录 \(\rm Lucas\) 定理的证明吧

    对于一个质数 \(p\)\(\le p\)\(n\)\(\binom pn\mod p\) 不为 \(0\) 当且仅当 \(n=p\) 或者 \(n=0\)

    这时可以发现

    \[\begin{aligned}(ax+bx)^p&\equiv b^px^p+a^px^p&\mod p \\ &\equiv bx^p+ax^p&\mod p\\ \end{aligned}\]

    那么考察 \(\binom nm=[x^m](1+x)^n\) 并展开 \((1+x)^n\) 得到:

    \[\begin{aligned}(1+x)^n&\equiv(1+x)^{p\lfloor \frac{n}p\rfloor}(1+x)^{n\bmod p}&\mod p\\ &\equiv(1+x^p)^{\lfloor \frac{n}p\rfloor}(1+x)^{n\bmod p}&\mod p \end{aligned}\]

    发现前半部分在 \(x^{pi}\) 处有值,后半部分只在 \(x^{k}(k\in [0,p)\) 处有值,那么直接变成两个子问题,写作

    \[\binom{\lfloor\frac{n}p\rfloor}{\lfloor\frac{m}p\rfloor}\binom{n\bmod p}{m\bmod p} \]

    根据唯一分解定理,问题变成了求解 \(\binom nm\mod p^k\)\(k\) 不一定为 \(1\),剩下的使用朴素 \(\rm CRT\) 合并即可

    \(\binom nm\) 展开,并提出 \(n,m,(n-m)\) 的阶乘中 \(p\) 的指数(逐个除就是 \(\Theta(\log)\) 的),剩下的也可以求逆元了

    剩余的项中包含且限于:\(\lfloor\frac{n}{p^k} \rfloor\)\([1,p^k]\) 中所有不是 \(p\) 的倍数的数的乘积,\([1,n\bmod p^k]\) 中不是 \(p\) 的倍数的乘积 和 \(\frac{n}{p}\) 的阶乘留下递归

    做完了!

    Code Display
    int n,Mod,x,y;
    const int N=1e6+10;
    map<int,int> fac;
    map<int,vector<int> >ee;
    inline int calc(int n,int pi,int pk){
    	if(n<=1) return 1;
    	return ksm(fac[pk],n/pk,pk)*ee[pk][n%pk]%pk*calc(n/pi,pi,pk)%pk;
    }
    inline void prepare(int pi,int pk){
        if(n>=pk){
            int res=1;
            for(int i=2;i<pk;++i) if(i%pi) ckmul(res,i,pk);
            fac[pk]=res;
        }
        vector<int> ii; ii.resize(min(n+1,pk));
        ii[0]=ii[1]=1;
        int up=ii.size();
        for(int i=2;i<up;++i){
            if(i%pi) ii[i]=ii[i-1]*i%pk;
            else ii[i]=ii[i-1];
        }
        ee[pk]=ii;
        return ;
    }
    inline void exgcd(int a,int b,int &x,int &y){
    	if(!b) return x=1,y=0,void();
    	exgcd(b,a%b,y,x);
    	y-=(a/b)*x;
    	return ;
    }
    inline int getinv(int n,int pk){
    	int x,y; exgcd(n,pk,x,y);
    	x=(x%pk+pk)%pk;
    	return x;
    }
    inline int C(int n,int m,int pi,int pk){
    	int up=calc(n,pi,pk),k=0;
    	int d1=calc(m,pi,pk),d2=calc(n-m,pi,pk);
    	for(int i=n;i;i/=pi) k+=i/pi;
    	for(int i=m;i;i/=pi) k-=i/pi;
    	for(int i=n-m;i;i/=pi) k-=i/pi;
    	return up*getinv(d1,pk)%pk*getinv(d2,pk)%pk*ksm(pi,k,pk)%pk;
    }
    inline int CRT(int b,int mod){
    	return (b*getinv(Mod/mod,mod)%Mod*(Mod/mod))%Mod;
    }
    int pi[N],pk[N],num;
    inline int exLucas(int n,int m){
        int res=0;
    	rep(i,1,num){
    		res+=CRT(C(n,m,pi[i],pk[i]),pk[i]); 
    		res%=Mod;
    	}
    	return res;
    }//sigma a_i M_i t_i
    signed main(){
        n=read(); Mod=read(); x=abs(read()),y=abs(read());
        int tmp=Mod;
    	for(int i=2;i*i<=tmp;++i) if(tmp%i==0){
    	    pk[++num]=1; pi[num]=i;
    		while(tmp%i==0) pk[num]*=i,tmp/=i;
    		prepare(pi[num],pk[num]);
    	}
    	if(tmp>1) pi[++num]=tmp,pk[num]=tmp,prepare(tmp,tmp);
        int ans=0;
        for(int lef=0;lef<=n;++lef){
            int rig=lef+x,sy=n-lef*2-x;
            if(((y+sy)&1)||y>sy) continue;
            int up=(y+sy)/2,down=sy-up;
            ckadd(ans,mul(exLucas(n,up),mul(exLucas(n-up,down),exLucas(n-sy,lef),Mod),Mod),Mod);
        }
        print(ans);
    	return 0;
    }
    
  • 相关阅读:
    Button 样式设置
    WPF 运行报错:在使用 ItemsSource 之前,项集合必须为空。
    c# List 按条件查找、删除
    c# WPF DataGrid设置一列自增一
    C# WPF DataGrid去掉最左侧自动生成一列
    int 转换成定长的 byte数组
    字节数组 byte[] 与 int型数字的相互转换
    [ c# ] int 类型转换为固定长度的字符串
    ListView 绑定 字典
    不能引用的文件,却需要在程序底层使用的文件 的存放位置
  • 原文地址:https://www.cnblogs.com/yspm/p/15759942.html
Copyright © 2011-2022 走看看