神秘 Math

经历了 lxl 讲课写题解的洗礼。

这一次 cjl 来讲课我是真的懒了,不是很想写题解,但是有些题又非常的浩,所以这里的题解有可能存在不完全不及时的情况。


总结等心情好(大概率是周天)再写的。


多项式点值与插值


P4463 [集训队互测 2012] calc

一个序列 \(a_1,a_2,\dots,a_n\) 是合法的,当且仅当:

  • \(a_1,a_2,\dots,a_n\) 都是 \([1,k]\) 中的整数。
  • \(a_1,a_2,\dots,a_n\) 互不相等。

一个序列的值定义为它里面所有数的乘积,即 \(a_1\times a_2\times\dots\times a_n\)

求所有不同合法序列的值的和对 \(p\) 取模后的结果。两个序列不同当且仅当他们任意一位不同。

\(k \le 10 ^ 9\)\(n \le 500\)\(p \le 10 ^ 9\),保证 \(p\) 为素数,保证 \(n + 1 < k < p\)


考虑答案是

\[[x^n](1+x)(1+2x)\cdots(1+Ax) \]

而如果没有互不相同的性质,答案应该是

\[\left( \frac {A(A+1)} 2 \right)^n \]

这是一个 \(2n\) 次多项式,我们知道,如果两个多项式,一个东西在任何地方都大于另一个东西,它的次数一定高。

所以我们就知道了这道题的多项式一定是一个关于 \(A\) 的低于 \(2n\) 的多项式。

那么我们对于 \(A = 1,2,\cdots,2n+1\) 都 dp 出答案,最后直接插值即可,时间复杂度 \(\mathcal O(n^2)\)代码


P10008 [集训队互测 2022] Range Minimum Element

有一个长度为 \(n\),值域为 \([1,c]\) 的正整数序列 \(a\)。给定 \(m\) 个区间 \([l_i,r_i]\),设长度为 \(m\) 的序列 \(b\) 满足 \(\forall i\in [1,m],b_i=\min\limits_{j=l_i}^{r_i}\{a_j\}\)。求出 \(a\) 在范围内任意取的情况下共能得到多少种不同的 \(b\)。答案对 \(998244353\) 取模。

\(1\le n\le 100,1\le m\le\dfrac{n(n+1)}{2},1\le c<998244353,\forall i\in [1,m],1\le l_i\le r_i\le n\)。保证给定的 \(m\) 个区间两两不同。

之前看 Hanghang 卷了这道题就比较想做,但是直到今天讲到这道题才做。


容易发现题目中给了一个 \(a \to b\) 的映射,但是会算重,所以我们考虑设计一种映射回去,也就是 \(b \to a\)

这种思路也可以理解成去 check 一个 \(b\) 是否合法。

考虑贪心,从大到小考虑每一个 \(b_i\),并且 \(a\) 序列最开始全部是 ?

对于当前的那些 \(b_i = x\) 的区间,如果该区间 \([l,r]\) 中没有 ?,则不合法;反之,我们把所有 ? 都填成 \(x\)

最后如果序列还剩下 ? 就把它们填成 \(1\)

那么这样我们就得到了一种 \(b \to a\) 的映射,同时也就是一种 check \(b\) 的方法。


考虑对这个东西进行区间 dp,我们从小往大扫值域。

假设当前扫到 \(x\),包含已经填的位置的 \([l_i,r_i]\) 它们的 \(b_i\) 一定是 \(\le x\) 的,反之没有填的那些连续段,对于那些区间的 \(b_i \gt x\)

\(ok_{l,r}\) 表示 \([l,r]\) 是否会被一些 \(b_i\) 完全覆盖(这里只用到 \(l_i \ge l,r_i \le r\) 的区间),\(f_{l,r,x}\) 表示当前扫到 \(x\),只考虑区间 \([l,r]\) 内部的情况,转移分两种

  • 当前 \([l,r]\) 区间没有 \(b_i = x\) 的元素,于是 \(f_{l,r,x} \leftarrow ok_{l,r} \times f_{l,r,x+1}\)

  • 存在 \(a_i = x\) 的元素,那么我们考虑枚举第一个位置 \(i\),则前面的区间就不能再用 \(x\) 了,就有

    \[f_{l,r,x} \leftarrow ok_{l,i-1} \times f_{l,i-1,x+1} \times f_{i+1,r,x} \]

这样就做到了 \(\mathcal O(n^3V)\) 的时间复杂度。


这时候就要充分发扬人类智慧,我们发现值域相当大,那么会不会 \(f_{l,r}\) 是关于 \(x\) 的一个 \(len=r-l+1\) 次多项式呢?

这个想法的来源在于我们转移相当简单,把 \(ok_{l,r}\) 忽略掉,那么转移就只有乘法和复制。

发现这个东西根据 归纳法 是非常容易证明的,所以我们只需要记录最大的 \(n+1\) 个元素,然后算出它们的 \(f_{l,r,x}\),最后直接插值出这个多项式就可以了。

那么时间复杂度 \(\mathcal O(n^4)\)代码

Code
int main(){
  /*2025.4.16 H_W_Y P10008 [集训队互测 2022] Range Minimum Element 映射 + 值域 + 区间 dp + 插值*/
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  cin>>n>>m>>c;
  for(int i=1;i<=m;i++) cin>>L[i]>>R[i];
  for(int l=1;l<=n;l++) for(int r=l;r<=n;r++){
	ok[l][r]=1;
	for(int i=l-1;i<=r+1;i++) d[i]=0;
	for(int i=1;i<=m;i++) if(L[i]>=l&&R[i]<=r) ++d[L[i]],--d[R[i]+1];
    for(int i=l;i<=r;i++){
	  d[i]+=d[i-1];
	  if(!d[i]) ok[l][r]=0;
	}
  }

  lim=n+1;
  for(int x=1;x<=lim;x++){
	for(int len=1;len<=n;len++){
	  for(int l=1,r=len;r<=n;++l,++r){
		if(ok[l][r]) f[l][r][x]=f[l][r][x-1];
        add(f[l][r][x],l==r?1:f[l+1][r][x]);
		if(ok[l][r-1]) add(f[l][r][x],f[l][r-1][x-1]);
		for(int i=l+1;i<r;i++) if(ok[l][i-1]) add(f[l][r][x],mul(f[l][i-1][x-1],f[i+1][r][x]));
	  }
	}
	val[x]=f[1][n][x];
  }
  if(c<=lim) cout<<val[c]<<'\n',exit(0);

  for(int i=1;i<=lim;i++){
	int tmp=val[i];
	for(int j=1;j<=lim;j++) if(i!=j) tmp=mul(tmp,mul(qpow(dec(j,i)),dec(j,c)));
	add(ans,tmp);
  }
  cout<<ans;
  return 0;
}

集合幂级数


P11458 [USACO24DEC] All Pairs Similarity P

Farmer John 的 \(N\)\(1\le N\le 5\cdot 10^5\))头奶牛都被分配了一个长度为 \(K\) 的非全零位串(\(1\le K\le 20\))。不同的奶牛可能被分配到相同的位串。

两个位串的 Jaccard 相似度定义为它们的按位与的结果中 \(\texttt{1}\) 的数量除以它们的按位或的结果中 \(\texttt{1}\) 的数量。例如,位串 \(\texttt{11001}\)\(\texttt{11010}\) 的 Jaccard 相似度为 \(2/4\)

对于每头奶牛,输出她的位串与每头奶牛(包括她自己)的位串的 Jaccard 相似度之和,对 \(10^9+7\) 取模。具体地说,如果总和等于一个有理数 \(a/b\),其中 \(a\)\(b\) 是互质的整数,则输出范围 \([0,10^9+7)\) 内的唯一整数 \(x\),使得 \(bx-a\)\(10^9+7\) 整除。

容斥好题,我终于又一次理解到了 dp 容斥系数的精妙。


这个东西等价于求什么?

也就是

\[\forall i,\sum_{j=1}^n \frac {|a_i \land a_j|} {|a_i \or a_j|} \]

我们把 \(\land\) 转化一下,变成 \(|a_i|+ |a_j|-|a_i \or a_j|\),那么问题就变成了

\[\forall i,|a_i| \sum_{j=1}^n \frac 1 {|a_i \or a_j|} + \sum_{j=1}^n \frac {|a_j|} {|a_i \or a_j|} -n \]

好,接下来我们考虑第一项的求法,对于第二项,由于是线性变换,所以我们只需要改变一下之前过程中的系数就可以了。

考虑类似推莫比乌斯反演的时候用的技巧

\[\sum_S \sum_{j=1}^n [a_i \mid a_j =S] \frac 1 {|S|} \]

很容易想到容斥,我们认为大小为 \(x\) 的数(\(1\) 的个数)的容斥系数是 \(w_x\),于是就有

\[\sum_S \sum_{j=1}^n [a_i \subseteq S,a_j \subseteq S] w_{|S|} \]

相当于是枚举 \(S\),统计 \(a_i |a_j \subseteq S\) 的方案数。

那么如何求容斥系数,发现对于一个集合大小 \(x\),我们的容斥系数其实满足

\[\frac 1 x = \sum_{y=x}^K w_y \binom {K-x}{y-x} \]

那么这就就可以从大到小 \(K^2\) 的时间复杂度推出每一个容斥系数 \(w_x\) 了!

而前面的 \(a_j \subseteq S\)\(a_i \subseteq S\) 可以用正着和反着的高维前缀和完成,总的时间复杂度 \(\mathcal O(K2^K)\)

没有看懂题解,但是自己写出来并且过了感觉非常牛!代码

Code
  cin>>n>>K,init();
  for(int i=1;i<=n;i++) cin>>a[i];

  for(int i=1;i<=n;i++) ++g[a[i]],f[a[i]]+=pc(a[i]);
  for(int i=0;i<K;i++) for(int S=0;S<(1<<K);S++) if(S>>i&1) add(f[S],f[S^(1<<i)]),add(g[S],g[S^(1<<i)]);
  for(int i=K;i>=1;i--){
	w[i]=inv[i];
	for(int j=i+1;j<=K;j++) del(w[i],mul(w[j],C[K-i][j-i]));
  }
  for(int S=0;S<(1<<K);S++) f[S]=mul(f[S],w[pc(S)]),g[S]=mul(g[S],w[pc(S)]);
  for(int i=0;i<K;i++) for(int S=0;S<(1<<K);S++) if(S>>i&1) add(f[S^(1<<i)],f[S]),add(g[S^(1<<i)],g[S]);
  for(int i=1;i<=n;i++) cout<<dec(adc(mul(pc(a[i]),g[a[i]]),f[a[i]]),n)<<'\n';

数论相关


P11846 [USACO25FEB] Transforming Pairs P

回答 \(Q\)\(1\le Q\le 10^5\))个独立查询,每个查询的形式如下:

给定四个整数 \(a\)\(b\)\(c\)\(d\)\(-10^{18}\le a,b,c,d\le 10^{18}\))。在一次操作中,你可以执行 \(a\mathrel{+}=b\),或 \(b\mathrel{+}=a\)。求将 \((a,b)\) 转变为 \((c,d)\) 所需要的最小操作次数,或者如果不可能完成,输出 \(-1\)

别骂了,我代码是贺的。


首先考虑 \(a,b\ge 0\) 的情况,显然一定需要 \(c,d \ge 0\),发现对于 \(c,d\) 而言,由于我们每次是减,并且一旦减到负数就回不来了。

所以如果 \(c \neq d\),那么 \(c,d\) 的走法是 唯一 的,所以我们直接对 \((c,d)\) 进行类似辗转相除的倒推,每次 check 过程中是否走到了 \((a,b)\) 即可。

对于其它的情况,如果 \(a\ge 0,b \lt 0\),那么我们考虑如果 \(c \le 0,d \ge 0\) 一定是无解的。

接下来再分两种情况考虑。

  • \(c\ge 0,d \lt 0\),发现我们同样不会走到其它的状态中去,那么等价于 \((c,-d) \to (a,-b)\),用第一种情况解决。

  • \(c,d \ge 0\),考虑过程中一定存在一个时刻,\(a\gt 0,b \lt 0\) 变成了 \(a,b\ge0\),然后就和第一种情况一样了。

    我们找到这个位置,每次一定是在一个等差数列结尾的位置?

    所以我们把 \((c,d)\) 的倒推过程记录下来,再去做 \((a,b)\) 的正推,去找到那个位置即可。

时间复杂度 \(\mathcal O(q \log V)\),具体实现看代码。代码

Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back

const ll inf=4e18;
ll A,B,C,D;

void NO(){cout<<"-1\n";}

void YES(ll x=0){cout<<x<<'\n';}

ll calc(ll A,ll B,ll C,ll D){
  if(C<0||D<0) return -1;
  if(A==C&&B==D) return 0;
  ll res=0; 
  while(C&&D){
	if(C<D) swap(A,B),swap(C,D);
	if(B==D&&C>=A&&(C-A)%D==0) return res+(C-A)/D;
	if(C%D==0&&A==D&&!B) return res+C/D;
	res+=C/D,C%=D;
  }
  return A==C&&B==D?res:-1;
}

ll F(ll A,ll B,ll C,ll D){
  ll res=calc(A,B,C,D);
  return ~res?res:inf;
}

void SOLVE(){
  cin>>A>>B>>C>>D;
  if(A<=0&&B<=0) A=-A,B=-B,C=-C,D=-D;
  if(A>=0&&B>=0) return YES(calc(A,B,C,D));
  if(A<0) A=-A,B=-B,C=-C,D=-D;
  if(C<0&&D>0) return NO();
  if(C>=0&&D<=0) return YES(calc(C,-D,A,-B));
  if(C<0) A=-A,B=-B,C=-C,D=-D,swap(A,B),swap(C,D);
  
  vector<array<ll,3> > S;
  for(ll x=C,y=D,z=0;x&&y;){
	if(y>=x) z+=y/x,y%=x;
	else S.pb({y,x,z}),z+=x/y,x%=y;
  }
  
  ll res=0,ans=inf;
  while(A>0&&B<0){
	if(A+B==0){ans=min(ans,res+1+F(A,0,C,D));break;}
    if(A+B<0){res+=(-B)/A,B=-((-B)%A);continue;}
    for(auto t:S){
	  ll y=t[0],mx=t[1];
	  if(y<A&&(A-y)%(-B)==0){
		ll k=(A-y)/(-B),x=A+(k-1)*B;
		if(x<=mx&&(mx-x)%y==0) ans=min(ans,res+t[2]+k+(mx-x)/y);
	  }
	}
	if(A%(-B)==0) ans=min(ans,res+A/(-B)+F(-B,0,C,D));
	res+=A/(-B),A=A%(-B);
  }
  if(A>=0&&B>=0) ans=min(ans,res+F(A,B,C,D));
  cout<<(ans==inf?-1:ans)<<'\n';
}

int main(){
  /*2025.4.16 H_W_Y P11846 [USACO25FEB] Transforming Pairs P gcd + 辗转相除*/
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  int _;cin>>_;
  while(_--) SOLVE();
  return 0;
}

Powerful Number 筛

很大的收获。

Min_25 筛的主要思想就是按照最大质因子的出现次数是否 \(\ge 2\) 来分类,进行一个神秘的均摊。


基础的思路就是我们要求一个 积性函数 \(f(x)\) 的前缀和,那么我们考虑去构造 积性函数 \(g\),满足

  • \(g\) 容易求前缀和。
  • 对于任意质数 \(p\),满足 \(f(p)=g(p)\)

然后得到函数 \(h=f/g\),其中除法是 狄利克雷卷积逆运算,也就是 \(f=h *g\),容易发现 \(h\) 也是 积性函数

因此对于一个质数 \(p\),我们有 \(f(p) = h(1)g(p)+g(1)h(p) = f(p)+h(p)\),那么 \(h(p)=0\)

所以 \(h\) 只在 PN 的位置有值。

这里的 PN 就是名字中的 Powerful Number,一个数 \(x\) 是 PN 当且仅当每个质因数次数 \(\ge 2\)

可以证明 PN 的数量不超过 \(\mathcal O(\sqrt n)\),证明是因为每一个 PN 都可以表示成 \(a^2b^3\) 的形式,然后对它做一些积分可以证明(这里不详细展开,想知道可以看 laofu 课件)。


基础做法就是

\[\sum_{i=1}^n f(i) = \sum_{i=1}^n h(i) \sum_{j=1}^{\left\lfloor \frac n i \right\rfloor} g(j)= \sum_{i \in PN} h(i) \sum_{j=1}^{\left\lfloor\frac n i \right\rfloor} g(j) \]

枚举 PN 的时间复杂度是 \(\mathcal O(\sqrt n)\) 的,然后只要合理的构造出 \(g\) 并且能快速计算前缀和即可。

而对于 \(h\) 的计算,有些题目是可以直接推出简单的形式,而有些不可以。但这也丝毫不妨碍,因为我们用 \(f\)\(h\) 的过程其实是可以每次用代码实现的,就可以在枚举 PN 的过程中完成。

接下来我们通过一些题来理解。


UOJ578 ULR Round1 校验码 – 部分分

给定积性函数 \(f(x)\) 满足 \(f(p^k)=p^{c\left\lfloor \frac k 2 \right \rfloor}\),求 \(\sum_{i=1}^n f(i)\)

\(n,c\) 都是读入的数,\(n \le 1.2 \times 10^{11}\)


考虑使用 PN 筛,我们知道 \(g(p)=f(p)=1\),那么不妨设计这个 \(g\) 为全 \(1\) 函数,那么我们尝试推 \(h\)

\[\begin{aligned} f(p) & = h(1)g(p) + g(1) h(p) =1 & \to h(p)=0 \\ f(p^2) & = h(1) g(p^2) + h(p) g(p) + h(p^2) g(1) & \to h(p^2)= p^c-1 \\ f(p^3) & =h(1) g(p^3) + h(p)g(p^2) + h(p^2) g(p) + h(p^3) g(1) & \to h(p^3)=0 \\ \cdots \end{aligned} \]

所以,我们找到规律:

\[h(p^k) = [k \bmod 2=0] (p^{c\frac k 2}- p^{c (\frac k2-1)}) \]

也就是说 \(h\) 只会在平方处有值。


那么答案就是

\[\sum_{i=1}^n f(i) = \sum_{i=1}^{\sqrt n} h(i^2) \left\lfloor \frac n {i^2} \right\rfloor \]

而对于平方处的值,我们可以通过爆搜过程中处理出来。

以上时间复杂度 \(\mathcal O(\sqrt n)\)代码

注意积性函数的处理技巧,可以在线性筛里面预处理出最小的质因子是什么。


P5325 【模板】Min_25 筛

定义积性函数 \(f(x)\),且 \(f(p ^ k) = p ^ k(p ^ k - 1)\)\(p\) 是一个质数),求

\[\sum_{i = 1} ^ n f(i) \]

\(10 ^ 9 + 7\) 取模。\(1\le n\le 10^{10}\)


往 PN 筛上面尝试一下,那么也就是构造一个 积性函数 \(g\),满足 \(g(p) = f(p) = p(p-1)\)

在质数位置与 \(p-1\) 相关,这让我们联想到 \(\varphi(p)\),也就是我们去设计 \(g(x) = x \varphi(x)\)

我们尝试推 \(h\),推到过程没有放上来

\[\begin{cases} h(p) =0 \\ h(p^2) = p^3-p^2 \\ h(p^3) = 2(p^4-p^3) \\ \cdots \end{cases} \]

也就是说 \(g(p^k) = p^{2k-1} (p-1)\),所以

\[p^k(p^k-1) = \sum_{i=0}^k p^{2i-1}(p-1) h(p^{k-i}) \]

得到

\[h(p^k)=(k-1)(p^{k+1}-p^k) \]


那么对于 \(g\) 的前缀和,我们考虑 杜教筛,将 \(g\) 卷上 \(ID\),那么得到的是 \(ID_2\)

也就是

\[\sum_{d \mid n} d \varphi(d) \frac n d= n^2 \]

直接杜教筛即可。


PN 筛的过程直接暴力枚举 PN,由于式子是

\[\sum_{i=1}^n f(i) = \sum_{i \in PN} h(i) \sum_{j=1}^{\left\lfloor \frac n i \right\rfloor} g_j \]

每次求的是 \(G \left(\left\lfloor\frac n i \right \rfloor\right)\),这在杜教筛的求 \(n\) 的前缀和中都可以求出来,所以时间复杂度是 \(\mathcal O(\sqrt n + n^{\frac 2 3})\)代码

Code
il void init(){
  phi[1]=1;
  for(int i=2;i<N;i++){
	if(!vis[i]) prm[++cnt]=i,phi[i]=i-1;
	for(int j=1;j<=cnt&&prm[j]*i<N;j++){
	  vis[prm[j]*i]=1;
	  if(i%prm[j]==0){phi[i*prm[j]]=phi[i]*prm[j];break;}
	  phi[i*prm[j]]=phi[i]*(prm[j]-1);
	}
  }
  for(int i=1;i<N;i++) g[i]=adc(g[i-1],mul(i,phi[i]));
}

il int S(ll l,ll r){return 1ll*(l+r)%H*(r-l+1)%H*inv2%H;}

il int G(ll n){
  if(n<N) return g[n];
  if(mp_g.count(n)) return mp_g[n];
  int res=1ll*n%H*(n+1)%H*(2*n+1)%H*inv6%H;
  for(ll l=2,r;l<=n;l=r+1) r=n/(n/l),del(res,mul(S(l,r),G(n/l)));
  return mp_g[n]=res;
}

il void PN(int id,ll x,int h,int op=0){
  if(op) add(ans,mul(h,G(n/x)));
  if(id>cnt||sq(prm[id])>n/x) return;
  PN(id+1,x,h),x*=1ll*prm[id];
  for(ll o=sq(prm[id]),k=2;;k++,o*=1ll*prm[id]){
    x*=1ll*prm[id];
    if(x>n) break;
    PN(id+1,x,1ll*o%H*(prm[id]-1)%H*(k-1)%H*h%H,1);
  }
}

LOJ6053 简单的函数

某一天,你发现了一个神奇的函数 \(f(x)\),它满足很多神奇的性质:

  1. \(f(1)=1\)
  2. \(f(p^c)=p \oplus c\)\(p\) 为质数,\(\oplus\) 表示异或)。
  3. \(f(ab)=f(a)f(b)\)\(a\)\(b\) 互质)。

你看到这个函数之后十分高兴,于是就想要求出 \(\sum\limits_{i=1}^n f(i)\)

由于这个数比较大,你只需要输出 \(\sum\limits_{i=1}^n f(i) \bmod (10^9+7)\)

\(1 \le n \le 10^{10}\)


这里给出 PN 筛的做法,或许后面用 Min_25 会更加简单一点。

考虑这里给的 \(f(p)=g(p)\)

\[f(p) = \begin{cases} p+1 & p=2 \\ p-1 & p 是奇质数 \end{cases} \]

\(p-1\) 让人想到去把 \(g\) 构造成 \(\varphi\),但是 \(p=2\) 的时候 \(f(p)=3\) 让人非常烦。

但是这只存在于这一种情况,我们考虑加上一个特判,由于积性函数涉及到的都是乘法操作,所以我们构造的 \(g\)

\[g(x) = \begin{cases} 3 \varphi(x) & 2 \mid x \\ \varphi(x) & 2 \nmid x \end{cases} \]

也就是分了奇数和偶数两种情况,容易发现 \(g\)积性函数


现在考虑根据 \(f,g\)\(h\)

你发现这个东西并不好推,因为 \(\oplus\)\(\varphi\) 根本就走不到一起来。

所以我们把这一部分留给代码实现。


最后的问题就是如何求 \(G(n) = \sum_{i=1}^n g(i)\) 了。

推式子

\[\begin{aligned} G(n) = & \sum_{i=2,2 \mid i}^n 3 \varphi(i) + \sum_{i=1,2 \nmid i}^n \varphi(i) \\ = & \sum_{i=1}^n \varphi(i) + 2 \sum_{i=1}^{\left\lfloor \frac n 2\right \rfloor} \varphi(2i) \end{aligned} \]

所以我们去计算两个东西 \(S_1(n) = \sum_{i=1}^n \varphi(i),S_2(n) = \sum_{i=1}^n \varphi(2i)\),那么

\[G(n) = S_1(n) + 2S_2\left( \left\lfloor \frac n 2 \right\rfloor\right) \]

显然 \(S_1\) 是可以直接杜教筛的,而对于 \(S_2\),再把它做一个奇偶的分组。

\(n\) 是偶数时

\[\begin{aligned} S_2(n) = & \sum_{i=1}^n \varphi(2i) \\ = & \sum_{i=1}^{\frac n 2} \left( \varphi(2(2i-1)) + \varphi(2(2i)) \right) \\ = & \sum_{i=1}^{\frac n 2} (\varphi(2i-1)+2\varphi(2i)) \\ = & \sum_{i=1}^n \varphi(i) + \sum_{i=1}^{\frac n 2} \varphi(2i) \\ = & S_1(n) + S_2\left( \frac n 2 \right) \end{aligned} \]

\(n\) 是奇数时,同理可得

\[S_2(n) = S_1(n)+S_2\left(\frac {n-1}2 \right) \]

综上所述

\[S_2(n) = S_1(n) + S_2\left( \left\lfloor \frac n 2 \right\rfloor \right) \]

时间复杂度,还是 \(\mathcal O(\sqrt n + n^{\frac 23})\) 吧。

实现复杂度疑似是 \(\mathcal O(\sqrt n k)\) 的,其中 \(k\) 是指数?代码

没过,也没卡,不管了,我们还是待会儿来用 Min_25 筛完成吧。


分解质因数

你好。


AGC003D Anticube

给定 \(n\) 个数 \(s_i\),要求从中选出最多的数,满足任意两个数之积都不是完全立方数。

\(n \le 10^5,a_i \le 10^{10}\)

把每个数都分解出来两两配对,我们一定是选多的那一边。

然后就是如何分解的问题,发现我们只对 \(\le 2155\) 的数进行分解,那么最后剩下的一定是 \(p,p^2,pq\) 的形式,我们只需要对于第二者判一下就可以找到匹配的元素是多少了。

这样就做完了。代码


图计数


P5206 [WC2019] 数树

本题包含三个问题:

  • 问题 0:已知两棵 \(n\) 个节点的树的形态(两棵树的节点标号均为 \(1\)\(n\)),其中第一棵树是红树,第二棵树是蓝树。要给予每个节点一个 \([1, y]\) 中的整数,使得对于任意两个节点 \(p, q\),如果存在一条路径 \((a_1 = p, a_2, \cdots , a_m = q)\) 同时属于这两棵树,则 \(p, q\) 必须被给予相同的数。求给予数的方案数。
    • 存在一条路径同时属于这两棵树的定义见「题目背景」。
  • 问题 1:已知蓝树,对于红树的所有 \(n^{n-2}\) 种选择方案,求问题 0 的答案之和。
  • 问题 2:对于蓝树的所有 \(n^{n-2}\) 种选择方案,求问题 1 的答案之和。

提示:\(n\) 个节点的树一共有 \(n^{n-2}\) 种。

在不同的测试点中,你将可能需要回答不同的问题。我们将用 \(\text{op}\) 来指代你需要回答的问题编号(对应上述 0、 1、 2)。

由于答案可能很大,因此你只需要输出答案对 \(998, 244, 353\) 取模的结果即可。

\(3 \le n \le 10^5\)

又是陈江伦的题。


首先假设两棵树的边集为 \(E_1\)\(E_2\),容易发现答案就是 \(y^{n-|E_1 \cap E_2|}\)

然后 \(op=0\) 容易解决,同时我们把 \(y=1\) 特判掉,接下来开始讨论。


\(op=1\)

容易发现

\[\begin{aligned} ans = \sum_{E_2} y^{n-|E_1 \cap E_2|} \end{aligned} \]

考虑集合 经典容斥

\[f(S) = \sum_{T \subseteq S} \sum_{P \subseteq T} (-1)^{|T|-|P|} f(P) \]

这个容斥式子能够很好的把 \(=S\) 转化为 \(\subseteq S\),于是上述我们的答案就变成

\[\begin{aligned} ans = & \sum_{E_2} y^{n-|E_1\cap E_2|} \\ = & \sum_{E_2} \sum_{T \subseteq E_1 \cap E_2} \sum_{P \subseteq T} (-1)^{|T|-|P|} y^{n-|P|} \\ = & \sum_{T \subseteq E_1} g(T) \sum_{P \subseteq T} (-1)^{|T|-|P|} y^{n-|P|} \\ = & \sum_{T \subseteq E_1} g(T) \sum_{i=0}^{|T|} \binom {|T|} i (-1)^{|T|-i} y^{|T|-i} y^{n-|T|} \\ = & \sum_{T \subseteq E_1} g(T) y^{n-|T|}(1-y)^{|T|} \end{aligned} \]

其中 \(g(T)\) 表示包含边集 \(T\) 的生成树个数。

如何计算 \(g(T)\)

有结论,设 \(k\)\(T\) 构成的连通块个数(\(k=n-|T|\)),则

\[g(T) = n^{k-1} \prod_{i=1}^k a_i \]

其中 \(a_i\) 表示第 \(i\) 个连通块的大小。

我们用 矩阵树 定理来证明一下这个东西(也可以用 prufer 序列证明)。

考虑生成树计数问题,我们构造的矩阵为 \(k \times k\) 的,一条边 \((i,j)\) 的权值为 \(a_i \times a_j\),也就相当于连了这么多条重边,那么答案就是这个矩阵的去掉一行一列的余子式

\[\begin{bmatrix} a_1 (n-a_1) & -a_1a_2 & -a_1a_3 & \cdots & -a_1a_k \\ -a_1a_2 & a_2(n-a_2) & -a_2a_3 & \cdots & -a_2a_k \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ -a_1a_k & -a_2 a_k& -a_3 a_k & \cdots & a_k(n-a_k) \end{bmatrix} \]

我们把最后一行和最后一列删掉,然后每一行分别提出一个 \(a_i\),得到答案是

\[\prod_{i=1}^{k-1} a_i \times det \left( \begin{bmatrix} n-a_1 & -a_2 & -a_3 & \cdots & -a_{k-1} \\ -a_1 & n-a_2 & -a_3 & \cdots & -a_{k-1} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ -a_1 & -a_2 & -a_3 & \cdots & n-a_{k-1} \end{bmatrix} \right) \]

对于后面的那个矩阵,我们考虑对它进行求行列式的基础操作,把后面 \(2 \sim k-1\) 列全部加到第一列来,会变成

\[\begin{bmatrix} a_k & -a_2 & -a_3 & \cdots & -a_{k-1} \\ a_k & n-a_2 & -a_3 & \cdots & -a_{k-1} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ a_k & -a_2 & -a_3 & \cdots & n-a_{k-1} \end{bmatrix} \]

然后用后面的 \(2 \sim k-1\) 行减去第一行,得到

\[\begin{bmatrix} a_k & -a_2 & -a_3 & \cdots & -a_{k-1} \\ 0 & n & 0 & \cdots & 0 \\ 0 & 0 & n & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \cdots & n \end{bmatrix} \]

所以行列式就是 \(a_k n^{k-2}\)

那么乘上之前的东西,我们知道答案就是

\[g(T) = n^{k-2} \prod_{i=1}^k a_i \]

得证。

于是现在原问题的答案就是

\[\begin{aligned} ans = & \sum_{T\subseteq E_1} y^{n-|T|} (1-y)^{|T|} n^{k-2} \prod_{i=1}^k a_i \\ = & \sum_{T \subseteq E_1} y^k (1-y)^{n-k} n^{k-2} \prod_{i=1}^k a_i \\ = & \frac {(1-y)^n} {n^2} \sum_{T \subseteq E_1} \prod_{i=1}^k \left( \frac {ny} {1-y} a_i \right) \end{aligned} \]

如何计算下面的式子?

我们考虑组合意义,也就是每一个连通块都需要选一个元素标记为关键点的方案数,标记一个点的代价是 \(K = \frac {ny} {1-y}\)

所以我们就可以对它进行树形 dp 了,设 \(f_{u,0/1}\) 表示以 \(u\) 为根的子树中,\(u\) 节点所在连通块是否已经标记关键点的答案。

那么初始 \(f_{u,0}=1,f_{u,1}=K\),转移的时候有

\[\begin{aligned} f_{u,0} \leftarrow & f'_{u,0} \times (f_{v,0}+f_{v,1}) \\ f_{u,1} \leftarrow & f'_{u,1} \times (f_{v,0},f_{v,1}) + f'_{u,0} \times f_{v,1} \end{aligned} \]

答案就是 \(f_{1,1}\)

这样就做完了,时间复杂度线性。


\(op=2\)

同样的推到方式,同样的容斥方式,我们得到

\[\begin{aligned} ans = & \sum_T g^2(T) y^{n-|T|} (1-y)^{|T|} \\ = & \frac {(1-y)^n} {n^4} \sum_{T} \prod_{i=1}^k \left( \frac {n^2y} {1-y}a_i^2 \right) \end{aligned} \]

考虑构造一个大小为 \(i\) 的连通块的生成函数,容易发现我们还需要乘上 \(a_i^{a_i-2}\),所以我们知道

\[F(x) = \sum_{i=1} \frac {n^2yi^i} {(1-y)i!} x^i \]

那么答案就是

\[ans=\frac {(1-y)^nn!} {n^4} [x^n] \sum_{k=1} \frac {1} {k!} F(x)^k \]

前面 \(\frac 1 {k!}\) 是因为连通块是无标号的,容易发现后面这一块就是 \(e^{F(x)}\),于是直接多项式 Exp 即可。

时间复杂度 \(\mathcal O(n \log n)\)代码


posted @ 2025-07-19 19:29  H_W_Y  阅读(30)  评论(0)    收藏  举报