开学前和这周主要练习了图论树论,还刷了一些atcoder上的题。新学了中国剩余定理,虚树。与树论有关的基本都是树上的dp,练了换根dp,dfn序,lca,关于树论还有几个比较难的算法(树分治,树分块,仙人掌,LCT,区间图等。。)这些我目前还没有接触到。另外刷atcoder练习了一些dp。
中国剩余定理的模板
int exgcd(int a,int b,int &x,int &y){
int d = a;
if(b){ d = exgcd(b, a%b, y, x);y -= (a/b)*x;}
else{x = 1;y = 0;}
return d;
}
int inv(int a,int mo){
int x,y;
int d = exgcd(a,n,x,y);
return d == 1 ? (x + mo)%mo : -1;//-1表示不存在,并对逆元调整
}
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int CRT(int n,int a[],int mo[]){//互质的时候
int mM = 1,W,x,y,res = 0;
for(int i = 1;i<=n;i++) mM *= mo[i];
for(int i = 1;i<=n;i++){
W = mM/mo[i];
exgcd(W,mo[i],x,y);
res = (res + x*W%mM*a[i])%mM;
}
return (res + mM)%mM;
}
bool merge(int a1,int m1,int a2,int m2,int &A,int &mo){
int c = (a2 - a1),d = gcd(m1,m2);
if(c % d) return 0;//gcd(m1,m2)|(a2 - a1)时才有解
c = (c%m2 + m2)%m2;//将c变为[0,m2)之间的数
c /= d;m1 /= d;m2 /= d;//两边除以d
c = c*inv(m1,m2) % m2;//将m1/d 移到右边
mo = m1*m2*d;// mo = m1 * m2 /d
A = (c*m1%mo*d%mo + a1)%mo;
return 1;
}
int EXCRT(int n,int a[],int mo[]){
int a1 = a[1],m1 = mo[1],A,Mo;
for(int i = 2;i<=n;i++){
if(!merge(a1,m1,a[i],mo[i],A,Mo)) return -1;//不合法
a1 = A;m1 = Mo;
}
return (a1 + m1)%m1;
}
虚树模板
int stk[N],top;//用一个栈记录虚树的一条树链
vector<int> G[N];//记录虚树
void build_tree(vector<int> &a){
sort(a.begin(),a.end(),[](int x,int y){
return dfn[x] < dfn[y];//对dfn进行排序从小的开始扫描
});
top = 0;
if(a[0] != 1) stk[top = 1] = 1;//先让根1入栈
for(int x : a){
int p = lca(stk[top],x);
if(p == stk[top]) {stk[++top] = x;continue;}//说明这个点在链上,入栈
while(top > 1 && dep[stk[top-1]] >= dep[p]){ //lca 不在栈里
G[stk[top-1]].push_back(stk[top]); // top-1 不在 top-2...也可能不在
top--;
}
if(stk[top] != p) { //新加的点和原来在栈里的点不在一条链上
G[p].push_back(stk[top]);
stk[top] = p;
}
stk[++top] = x;
}
while(top > 1) { //把栈里剩下的点连上
G[stk[top-1]].push_back(stk[top]);
top--;
}
}