【考试总结】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;
}
原文地址:https://www.cnblogs.com/yspm/p/15759942.html