exCRT&Prüfer
中国剩余定理
\(\texttt{Description}\)
求出最小的 \(x\)。
\(\texttt{Solution}\)
首先先将问题特殊化,如果只有两个数怎么做?
然后就可以得到:\(t_1\times a_1-t_2\times a_2=b_2-b_1\)
然后用 \(\text{exgcd}\) 找到最小的满足条件的自然数 \(t_2\) 就可以了。
然后如果我们把通解带回原方程组,就可以得到:
我们发现可以和下面的联立每次都要使后面一项尽可能的小。
所以这个 \(a_i\) 是质数是在迷惑什么?
void exgcd(__int128 x,__int128 y){
if (y==0){a=1;b=0;return ;}
exgcd(y,x % y);__int128 c=a;a=b;b=c-b*(x/y);
}
int main()
{
n=read();
for (i=1;i<=n;i++) A[i]=read(),B[i]=read(),B[i]%=A[i];
if (n==1){
ans=B[1];
printf("%lld\n",ans);return 0;
}
for (i=2;i<=n;i++){
exgcd(A[i],A[i-1]);
long long gd=__gcd(A[i],A[i-1]);
lft=A[i]/gd;rit=A[i-1]/gd;G=0;
if (B[i-1]-B[i]<0) a=-a,b=-b;
a=a*(abs(B[i-1]-B[i])/gd);b=b*(abs(B[i-1]-B[i])/gd);
if (a>=0) G=-(a/rit);
else G=(-a-1)/rit+1;
lft=a+G*rit;
if (i==n){ans=A[i]*lft+B[i];printf("%lld\n",ans);return 0;}
B[i]=(A[i]*lft+B[i]) % (A[i]/gd*A[i-1]);A[i]=A[i]/gd*A[i-1];
}
return 0;
}
Prüfer 序列
定义
长度为 \(n-2\),值域为 \([1,n]\) 的一种序列,可以表示 \(n\) 个点的带标号无根树。可以与 \(n\) 个点的完全图的生成树形成双射。
树到\(\texttt{Prüfer}\)
-
找到剩余图中编号最小的叶子节点
-
记录他在图中连接到的那个节点
-
重复 \(n-2\) 次
有一种显然的做法是每次把叶子节点取出,父亲节点 \(\texttt{deg}-1\) ,然后如果又变成了叶子节点就把他加到堆里,复杂度是 \(\mathcal O(n\log n)\)。
然后我们考虑到因为刚取出来的这个叶子节点是最小的,那么如果删去之后,父亲节点变成了叶子节点,并且编号比这个叶子节点要小,那么他一定可以直接选择,因为现存的叶子节点是没有比这个叶子节点要小的,然后复杂度就变成了 \(\mathcal O(n)\)。
\(\texttt{Prüfer}\)到树
我们注意到一个点的出现次数就是 \(\texttt{deg}-1\),那么 \(\texttt{Prüfer}\) 序列中没有出现过的点,就是叶子节点。然后就是将这些所有点加进去,每次取出最小的,如果 \(\texttt{Prüfer}\) 序列中一个点出现完了,也把这个点加进去继续做,这时候时间复杂度是 \(\mathcal O(n\log n)\)。
然后我们再看有没有单调性,是不是新的那个出现完的节点一定是最小的呢?
根据上面的序列构造的方法,可以发现这是显然的。
void Subtask1(){
for (i=1;i<n;i++) fa[i]=read(),deg[fa[i]]++;
now=1;
for (i=1;i<=n-2;i++){
while (deg[now]) now++;prufer[i]=fa[now];
for (;i<=n-2;i++){
deg[prufer[i]]--;
if ((deg[prufer[i]])||(prufer[i]>now)) break;
prufer[i+1]=fa[prufer[i]];
}now++;
}for (i=1;i<=n-2;i++) ans^=(i*prufer[i]);
printf("%lld\n",ans);
}void Subtask2(){
for (i=1;i<=n-2;i++) prufer[i]=read(),deg[prufer[i]]++;
prufer[n-1]=n;now=1;
for (i=1;i<=n-1;i++){
while (deg[now]) now++;fa[now]=prufer[i];
for (;i<=n-1;i++){
deg[prufer[i]]--;
if ((deg[prufer[i]])||(prufer[i]>=now)) break;
fa[prufer[i]]=prufer[i+1];
}now++;
}for (i=1;i<=n-1;i++) ans^=(i*fa[i]);
printf("%lld\n",ans);
}

浙公网安备 33010602011771号