广义斐波那契循环节

广义斐波那契数递推公式
$$f_i=af_{i-1}+bf_{i-2}(\mod p) (p是奇素数)$$

他的转移矩阵
$$
\left[
\begin{matrix}
a & b \
1 & 0
\end{matrix}
\right]^n

\left[
\begin{matrix}
f_{2} \
f_{1}
\end{matrix}
\right]\mod p=

\left[
\begin{matrix}
f_{n+2} \
f_{n+1}
\end{matrix}
\right]
$$

如果存在循环节则存在n使得
$$
\left[
\begin{matrix}
a & b \
1 & 0
\end{matrix}
\right]^n=
\left[
\begin{matrix}
k_1p+1 & k_2p+0 \
k_3p+0 & k_4p+1
\end{matrix}
\right]
$$

我们尝试把左边变成相似对角矩阵
先求特征值
$$
(\lambda-a)\lambda-b=0 \Leftrightarrow \lambda=\frac{a\pm\sqrt{a^2+4b}}{2}
$$

当且仅当$a^2+4b=0$的时候,$\lambda_1=\lambda_2$,易证尽管此时$\lambda$是二重特征值,但是它对应的特征向量只有一个,即上诉矩阵不可对角化,我们不考虑这种复杂的情况。
当$a^2+4b\neq0$的时候,两个特征向量分别为
$$
\left[
\begin{matrix}
\lambda_1 \
1
\end{matrix}
\right] 和
\left[
\begin{matrix}
\lambda_2 \
1
\end{matrix}
\right]
$$
那么就有了
$$
\left[
\begin{matrix}
a & b \
1 & 0
\end{matrix}
\right]=
\left[
\begin{matrix}
\lambda_1 & \lambda_2 \
1 & 1
\end{matrix}
\right]
\left[
\begin{matrix}
\lambda_1 & 0 \
0 & \lambda_2
\end{matrix}
\right]
\left[
\begin{matrix}
\frac{1}{\lambda_1-\lambda_2} & \frac{-\lambda_2}{\lambda_1-\lambda_2} \
\frac{-1}{\lambda_1-\lambda_2} & \frac{\lambda_1}{\lambda_1-\lambda_2}
\end{matrix}
\right]
$$
进而有了
$$
\left[
\begin{matrix}
\lambda_1 & \lambda_2 \
1 & 1
\end{matrix}
\right]
\left[
\begin{matrix}
\lambda_1^n & 0 \
0 & \lambda_2^n
\end{matrix}
\right]
\left[
\begin{matrix}
\frac{1}{\lambda_1-\lambda_2} & \frac{-\lambda_2}{\lambda_1-\lambda_2} \
\frac{-1}{\lambda_1-\lambda_2} & \frac{\lambda_1}{\lambda_1-\lambda_2}
\end{matrix}
\right]=
\left[
\begin{matrix}
k_1p+1 & k_2p+0 \
k_3p+0 & k_4p+1
\end{matrix}
\right]
$$
右乘$T$消掉那个一堆分数的矩阵
$$
\left[
\begin{matrix}
\lambda_1 & \lambda_2 \
1 & 1
\end{matrix}
\right]
\left[
\begin{matrix}
\lambda_1^n & 0 \
0 & \lambda_2^n
\end{matrix}
\right]=
\left[
\begin{matrix}
k_1p+1 & k_2p+0 \
k_3p+0 & k_4p+1
\end{matrix}
\right]
\left[
\begin{matrix}
\lambda_1 & \lambda_2 \
1 & 1
\end{matrix}
\right]
$$
乘开
$$
\left[
\begin{matrix}
\lambda_1^{n+1} & \lambda_2^{n+1} \
\lambda_1^{n} & \lambda_1^{n}
\end{matrix}
\right]=
\left[
\begin{matrix}
\lambda_1(k_1p+1)+k_2p & \lambda_2(k_1p+1)+k_2p \
\lambda_1k_3p+k_4p+1 & \lambda_2k_3p+k_4p+1
\end{matrix}
\right]
$$
###在这之后我们分两部分讨论
####$a^2+4b是二次剩余$
如果$a^2+4b$是二次剩余,那么$\lambda_1$和$\lambda_2$可以直接写成模意义下对应的整数,则上诉矩阵等式在$n=p-1$的时候根据费马小定理恒成立
####$a^2+4b不是二次剩余$
在这里,是绝对不能够直接取模的,因为$\lambda$中一旦包含了根号下非二次剩余,这里就是错的,我们不可以取模,直接用根式表达即可。
#####两矩阵相等条件1:
$$
\lambda^n=\lambda k_3p+k_4p+1
$$
先看$\lambda_1$
$$
\lambda_1=\frac{a+\sqrt{a^2+4b}}{2}=\frac{a}{2}+\frac{1}{2}\sqrt{a^2+4b}
$$

$$
(\frac{a}{2}+\frac{1}{2}\sqrt{a^2+4b})^n=(\frac{a}{2}+\frac{1}{2}\sqrt{a^2+4b})k_3p+k_4p+1
$$
分母有点难受,把它移到右边去
$$
(a+\sqrt{a^2+4b})^n=2^n* ((\frac{a}{2}+\frac{1}{2}\sqrt{a^2+4b})k_3p+k_4p+1)\
\sum_{i=0}^nC_n^ia^i\sqrt{a^2+4b}^{n-i}=2^n* (\frac{a}{2}k_3p+k_4p+1)+2^n* (\frac{1}{2}k_3p)\sqrt{a^2+4b}
$$
我们在这里引入一些概念,我们在实数领域解决这个问题,在实数领域,我们把数分为两部分来表示,一部分是有理数部分,称之为有理部,另一部分是无理数部分,称之为无理部,即$1+2\sqrt{16}$中,我们称1为有理部,2位无理部。
上式左边显然能够唯一表示为$x+y\sqrt{a^2+4b}$,那么两式相等的充要条件就是
$$
存在k_3,k_4使得x=2^n* (\frac{a}{2}k_3p+k_4p+1), y=2^n* \frac{1}{2}k_3p
$$
上面的式子的某个充分条件为
$$
\frac{x}{2^n} \equiv1 \mod p\
\frac{y}{2^n} \equiv0 \mod p
$$
更加具体一点如果n是(p-1)的倍数则下面的式子也是充分条件
$$
x \equiv1 \mod p\
y \equiv0 \mod p
$$
为了利用这点,我们保证后面n一定是p-1的倍数,让我们先遗忘掉这些充分条件

然后我们来看看这个规律,注意到$\sum_{i=0}^nC_n^ia^i\sqrt{a^2+4b}^{n-i}$中,当$n=p且i\neq0且i\neq n$的时候,$C_n^i|p$,所以
$$
x\equiv a^p \equiv a\
y\equiv \sqrt{a^2+4b}^p
$$

$$
\begin{aligned}
&(a+\sqrt{a^2+4b})^p
\=&(a+c_1p)+(\sqrt{a^2+4b}^{p-1}+c_2p)\sqrt{a^2+4b},c_1c_2是整数
\=& (a+c_1p)+((a^2+4b)^{\frac{p-1}{2}}+c_2p)\sqrt{a^2+4b}
\=& a+(a^2+4b)^{\frac{p-1}{2}}\sqrt{a^2+4b}+c_1p+c_2p\sqrt{a^2+4b}
\end{aligned}
$$
这时候因为$a^2+4b$是一个非二次剩余,所以上式可以表达为
$$
a-\sqrt{a^2+4b}+c_1p+c_2p\sqrt{a^2+4b}
$$
我们让他乘上$\frac{a+\sqrt{a^2+4b}}{2}$,他的无理部就彻底与0同余了,此时的$n=(p+1)$,在让这个数幂上$p-1$,他的有理部就与1同余了,并且我们达到了之前的约定,n是p-1的倍数,此时的$n=(p+1)(p-1)$
#####两矩阵相等条件2:
$$
\begin{aligned}
&\lambda^{n+1}=\lambda(k_1p+1)+k_2p\
\Leftrightarrow&\lambda^{n+1}=(\frac{a}{2}+\frac{1}{2}\sqrt{a^2+4b})(k_1p+1)+k_2p\
\Leftrightarrow&\lambda^{n+1}=(\frac{a}{2}+\frac{1}{2}\sqrt{a^2+4b})+\frac{a}{2}k_1p+\frac{1}{2}\sqrt{a^2+4b}k_1pk_2p+k_2p
\end{aligned}
$$
之前我们证明了$\lambda^{(p+1)(p-1)}$的有理部与1同余,无理部与0同余,这里显然$\lambda^{(p+1)(p-1)+1}$的有理部与$\frac{a}{2}$同余,无理部与$\frac{1}{2}$同余,
至于$\lambda_2$是同理的。

至此证明了当$a^2+4b$是二次剩余的时候,循环节至多为$n-1$,当$a^2+4b$不是二次剩余的时候,循环节至多为$n^2-1$
当$a^2+4b=0$的时候还有待挖掘

2019牛客多校9A

###name
the power of Fibonacci

###description
Amy asks Mr. B problem A. Please help Mr. B to solve the following problem.
Let Fi be fibonacci number.
$F_0 = 0, F_1 = 1, F_i = F_{i-1} + F_{i-2}$
Given n and m, please calculate
$\sum^n_{i=0}{F_i^m}$
As the answer might be very large, output it module 1000000000.

###input
The first and only line contains two integers n, m(1 <= n <= 1000000000, 1 <= m <= 1000).

###output
Output a single integer, the answer module 1000000000.

###sample input 1
5 5

###sample output 1
3402

###sample input 2
10 10

###sample output 2
696237975

###sample input 3
1000000000 1000

###sample output 3
641796875

###toturial
对$10^9$进行分解,$10^9=2^9*5^9$,然后打表,分别找到循环节的长度,最后用中国剩余定理合并

###code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
const int len5=7812500,len2=768;
int fib5[len5+1],fib2[len2+1];

int qpow(int a,int b,int mod){
int res=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1)res=1ll*res*a%mod;
return res;
}

void Exeuclid(ll a, ll& x, ll b, ll& y, ll c){
if (!b) { x = c / a, y = 0; }
else {
Exeuclid(b, x, a % b, y, c);
x -= a / b * y;
swap(x, y);
}
}

int merge(int x1,int p1,int x2,int p2){
// u*p1+x1=v*p2+x2
// u*p1-v*p2=x2-x1
ll u,v;
Exeuclid(p1,u,p2,v,x2-x1);
ll p=p1*p2;
return ((u*p1+x1)%p+p)%p;
}

int main(){
// cout<<1ll*len5/__gcd(len5,len2)*len2<<endl;
fib2[1]=fib2[2]=fib5[1]=fib5[2]=1;
rep(i,3,len2) fib2[i]=(fib2[i-1]+fib2[i-2])%512;
rep(i,3,len5) fib5[i]=(fib5[i-1]+fib5[i-2])%1953125;
int n,m; scanf("%d%d",&n,&m);
rep(i,1,len2) fib2[i]=(qpow(fib2[i],m,512)+fib2[i-1])%512;
rep(i,1,len5) fib5[i]=(qpow(fib5[i],m,1953125)+fib5[i-1])%1953125;
int ans2=(1ll*fib2[len2]*(n/len2)+fib2[n%len2])%512;
int ans5=(1ll*fib5[len5]*(n/len5)+fib5[n%len5])%1953125;
printf("%d\n",merge(ans2,512,ans5,1953125));
}

中国剩余定理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define I __int128
void exgcd(I a,I&x,I b,I&y,I c){ // assert(__gcd(a,b)==c)
if(b==0) x=c/a,y=0;
else exgcd(b,y,a%b,x,c),y-=a/b*x;
}

inline bool merge(I x1,I p1,I x2,I p2,I&x,I&p){
I a,b,d=__gcd(p1,p2);// ap1+x1=bp2+x2 a+k(p2/gcd)
if((x2-x1)%d!=0) return false;
exgcd(p1,a,p2,b,x2-x1);
p=p1/d*p2; //lcm
x=((a*p1+x1)%p+p)%p;//
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
static BigInteger[] exgcd(BigInteger a, BigInteger b, BigInteger c) { // ax+by=c res[0]=x,res[1]=y
if (b.compareTo(BigInteger.ZERO) == 0) return new BigInteger[]{c.divide(a), BigInteger.ZERO};
BigInteger[] r = exgcd(b, a.mod(b), c);
return new BigInteger[]{r[1], r[0].subtract(a.divide(b).multiply(r[1]))};
}

static BigInteger[] merge(BigInteger x1, BigInteger p1, BigInteger x2, BigInteger p2) {
BigInteger d = p1.gcd(p2);
if (x2.subtract(x1).mod(d).compareTo(BigInteger.ZERO) != 0) return null;
BigInteger[] r = exgcd(p1, p2, x2.subtract(x1));
BigInteger p = p1.divide(d).multiply(p2); // p=p1/d*p2
BigInteger x=r[0].multiply(p1).add(x1).mod(p).add(p).mod(p);
return new BigInteger[]{x, p};
}
}

虚树

虚树就是把树上一些节点拿下来重新建树,插入一些lca之类的点,deltree会删除一颗树,但不会删掉他的边,所以要注意边的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// tree 节点0不准使用
const int maxn=5e5+5;
int head[maxn];// point
int to[maxn*2],ew[maxn*2],nex[maxn*2],tot;// edge
inline void _addedge(int u,int v,int w){to[++tot]=v,ew[tot]=w,nex[tot]=head[u],head[u]=tot;}
inline void addedge(int u,int v,int w){_addedge(u,v,w),_addedge(v,u,w);}
void deltree(int rt,int father){// deltree() and also don't forget 还原tot
for(int i=head[rt];i;i=nex[i]) if(to[i]!=father) deltree(to[i],rt);
head[rt]=0;
}

// 树剖lca
int dep[maxn],dad[maxn],siz[maxn],son[maxn],dfn[maxn],chain[maxn],step;
void dfs1(int u,int father){// dfs(1,0)
siz[u]=1; son[u]=0; dad[u]=father; dep[u]=dep[father]+1;
for(int i=head[u];i;i=nex[i]){
if(to[i]==father) continue;
dfs1(to[i],u);
siz[u]+=siz[to[i]];
if(siz[to[i]]>siz[son[u]]) son[u]=to[i]; // don't care son=0 because siz[0]=0
}
}
void dfs2(int u,int s){// dfs(1,1) step=0 at begin
dfn[u]=++step; chain[u]=s;
if(son[u]) dfs2(son[u],s);
for(int i=head[u];i;i=nex[i]){
if(to[i]==dad[u]||to[i]==son[u]) continue;
dfs2(to[i],to[i]);
}
}
int getlca(int x,int y){
while(chain[x]!=chain[y]) {
if(dep[chain[x]]<dep[chain[y]]) swap(x,y);
x=dad[chain[x]];
}
return dep[x]<dep[y]?x:y;
}

// virtual tree
bool vt[maxn];// point
void buildvt(int*vert,int nums,int base){// vert -> [1,nums]
sort(vert+1,vert+nums+1,[](int x,int y){return dfn[x]<dfn[y];});
int top=0;
stk[++top]=1,vt[base+1]=vert[1]==1; // root
rep(i,vert[1]==1?2:1,nums){
int lca=getlca(vert[i],stk[top]);
if(lca==stk[top]) {stk[++top]=vert[i],vt[base+vert[i]]=true;continue;}//还在链上
while(dfn[lca]<=dfn[stk[top-1]]) addedge(base+stk[top],base+stk[top-1],0),top--;
if(lca!=stk[top]) addedge(base+stk[top],base+lca,0),stk[top]=lca,vt[base+lca]=false;
stk[++top]=vert[i],vt[base+vert[i]]=true;
}
while(top>=2){
addedge(base+stk[top],base+stk[top-1],0);
top--;
}
}

二次剩余

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
typedef long long ll;
struct cp{
static ll p,w;
ll x,y;// x+y\sqrt(w)
cp(ll x,ll y):x(x),y(y){}
cp operator*(cp rhs){
return cp((x*rhs.x+y*rhs.y%p*w)%p,(x*rhs.y+y*rhs.x)%p);
}
};
ll cp::p,cp::w;

cp qpow(cp a,ll b){
cp res(1,0);
for(;b;b>>=1,a=a*a) if(b&1)res=res*a;
return res;
}
ll qpow(ll a,ll b,ll p){
ll res=1;
for(;b;b>>=1,a=a*a%p) if(b&1)res=res*a%p;
return res;
}
ll sqrt(ll x,ll p){ // return sqrt(x)%p
if(x==0) return 0;
if(qpow(x,(p-1)/2,p)==p-1)return -1;
ll a=1,w=(1-x+p)%p;
while(qpow(w,(p-1)/2,p)!=p-1) ++a,w=(a*a-x+p)%p;
cp::w=w,cp::p=p;
return qpow(cp(a,1),(p+1)/2).x;
}

lct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int top,c[N][2],f[N],tim[N],sta[N],rev[N],val[N];
void ini(){
for(int i=0;i<=n;i++)c[i][0]=c[i][1]=f[i]=rev[i]=0,tim[i]=i,val[i]=2e9;
for(int i=n+1;i<=n+m;i++)c[i][0]=c[i][1]=f[i]=rev[i]=0,tim[i]=i,val[i]=R[i-n];
}
inline void pushup(int x){
tim[x]=x;
if(val[tim[c[x][0]]]<val[tim[x]]) tim[x]=tim[c[x][0]];
if(val[tim[c[x][1]]]<val[tim[x]]) tim[x]=tim[c[x][1]];
}
inline void pushdown(int x){
int l=c[x][0],r=c[x][1];
if(rev[x]){
rev[l]^=1;rev[r]^=1;rev[x]^=1;
swap(c[x][0],c[x][1]);
}
}
inline bool isroot(int x){return c[f[x]][0]!=x&&c[f[x]][1]!=x;}
inline void rotate(int x){
int y=f[x],z=f[y],xis=c[y][1]==x,yis=c[z][1]==y;//
if(!isroot(y)) c[z][yis]=x;//son
f[x]=z;f[y]=x;f[c[x][xis^1]]=y;//father
c[y][xis]=c[x][xis^1];c[x][xis^1]=y;//son
pushup(y);
}
inline void splay(int x){
top=1;sta[top]=x;//init stack
for(int i=x;!isroot(i);i=f[i])sta[++top]=f[i];//update stack
for(int i=top;i;i--)pushdown(sta[i]);//pushroad
while(!isroot(x)){
int y=f[x],z=f[y];
if(!isroot(y)) (c[y][0]==x)^(c[z][0]==y)?rotate(y):rotate(x);
rotate(x);
}pushup(x);
}
inline void access(int x){for(int t=0;x;t=x,x=f[x])splay(x),c[x][1]=t,pushup(x);}
inline int treeroot(int x){access(x);splay(x);while(c[x][0])x=c[x][0];return x;}
inline void makeroot(int x){access(x);splay(x);rev[x]^=1;}// 让x变成根
inline void cut(int x,int y){makeroot(x);access(y);splay(y);f[x]=c[y][0]=0;pushup(y);}
inline void link(int x,int y){makeroot(x);f[x]=y;}

2019牛客多校8E

###name
explorer

###description
Gromah and LZR have entered the fifth level. Unlike the first four levels, they should do some moves in this level.

There are nvertices and m bidirectional roads in this level, each road is in format (u,v,l,r) , which means that vertex u and v are connected by this road, but the sizes of passers should be in interval [l,r] . Since passers with small size are likely to be attacked by other animals and passers with large size may be blocked by some narrow roads.

Moreover, vertex 1 is the starting point and vertex n is the destination. Gromah and LZR should go from vertex 1 to vertex n to enter the next level.

At the beginning of their exploration, they may drink a magic potion to set their sizes to a fixed positive integer. They want to know the number of positive integer sizes that make it possible for them to go from 1 to n .

Please help them to find the number of valid sizes.

###input
The first line contains two positive integers n,m , denoting the number of vertices and roads.
Following m lines each contains four positive integers u,v,l,r , denoting a bidirectional road (u,v,l,r) .
$1≤n,m≤10^5 ,1≤u\lt v≤n,1≤l≤r≤10^9$

###output
Print a non-negative integer in a single line, denoting the number of valid sizes.

###sample input
5 5
1 2 1 4
2 3 1 2
3 5 2 4
2 4 1 3
4 5 3 4

###sample output
2

###hint
There are 2 valid sizes : 2 and 3.
For size 2, there exists a path 1→2→3→5.
For size 3, there exists a path 1→2→4→5.

###toturial
把l,r看作限制,从小到大枚举区间,则表现为删边加边,然后问图的联通情况。这可以用直接lct维护。

###code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include<bits/stdc++.h>
using namespace std;

static const int N=4e5+555;
int X[N],Y[N],L[N],R[N],sa[2][N];
int n,m;

int top,c[N][2],f[N],tim[N],sta[N],rev[N],val[N];
void ini(){
for(int i=0;i<=n;i++)c[i][0]=c[i][1]=f[i]=rev[i]=0,tim[i]=i,val[i]=2e9;
for(int i=n+1;i<=n+m;i++)c[i][0]=c[i][1]=f[i]=rev[i]=0,tim[i]=i,val[i]=R[i-n];
}
inline void pushup(int x){
tim[x]=x;
if(val[tim[c[x][0]]]<val[tim[x]]) tim[x]=tim[c[x][0]];
if(val[tim[c[x][1]]]<val[tim[x]]) tim[x]=tim[c[x][1]];
}
inline void pushdown(int x){
int l=c[x][0],r=c[x][1];
if(rev[x]){
rev[l]^=1;rev[r]^=1;rev[x]^=1;
swap(c[x][0],c[x][1]);
}
}
inline bool isroot(int x){return c[f[x]][0]!=x&&c[f[x]][1]!=x;}
inline void rotate(int x){
int y=f[x],z=f[y],xis=c[y][1]==x,yis=c[z][1]==y;//
if(!isroot(y)) c[z][yis]=x;//son
f[x]=z;f[y]=x;f[c[x][xis^1]]=y;//father
c[y][xis]=c[x][xis^1];c[x][xis^1]=y;//son
pushup(y);
}
inline void splay(int x){
top=1;sta[top]=x;//init stack
for(int i=x;!isroot(i);i=f[i])sta[++top]=f[i];//update stack
for(int i=top;i;i--)pushdown(sta[i]);//pushroad
while(!isroot(x)){
int y=f[x],z=f[y];
if(!isroot(y)) (c[y][0]==x)^(c[z][0]==y)?rotate(y):rotate(x);
rotate(x);
}pushup(x);
}
inline void access(int x){for(int t=0;x;t=x,x=f[x])splay(x),c[x][1]=t,pushup(x);}
inline int treeroot(int x){access(x);splay(x);while(c[x][0])x=c[x][0];return x;}
inline void makeroot(int x){access(x);splay(x);rev[x]^=1;}// 让x变成根
inline void cut(int x,int y){makeroot(x);access(y);splay(y);f[x]=c[y][0]=0;pushup(y);}
inline void link(int x,int y){makeroot(x);f[x]=y;}
inline void cut2(int i){
makeroot(X[i]);
if(treeroot(Y[i])!=X[i]) return;
cut(X[i],n+i),cut(Y[i],n+i);
}
inline void link2(int i){
makeroot(X[i]);
if(treeroot(Y[i])==X[i]) {// access(y) splay(y)
int p=tim[Y[i]]-n;
if(R[p]>=R[i]) return;// 这个非常重要
cut(X[p],n+p),cut(Y[p],n+p);
}
link(X[i],n+i),link(Y[i],n+i);
}

int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d%d%d",X+i,Y+i,L+i,R+i),sa[0][i]=sa[1][i]=i;
ini();

sort(sa[0]+1,sa[0]+1+m,[](int a,int b){return L[a]<L[b];});
sort(sa[1]+1,sa[1]+1+m,[](int a,int b){return R[a]<R[b];});
vector<int>disc;
for(int i=1;i<=m;i++) disc.push_back(L[i]),disc.push_back(R[i]+1);// [)
sort(disc.begin(),disc.end());
disc.erase(unique(disc.begin(),disc.end()),disc.end());

int ans=0;
for(int t=0,i=1,j=1;t<disc.size();t++){// [T,T+1)
while(i<=m&&L[sa[0][i]]==disc[t]) link2(sa[0][i++]);
while(j<=m&&R[sa[1][j]]+1==disc[t]) cut2(sa[1][j++]);
makeroot(1);if(treeroot(n)==1) ans+=disc[t+1]-disc[t];
}
cout<<ans<<endl;
}

树链剖分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const int maxn=1e5+5;
int to[maxn<<1],nex[maxn<<1],head[maxn],w[maxn],cnt;
void ini(){cnt=-1;for(int i=0;i<=n;i++) head[i]=-1;}
void add_edge(int u,int v){to[++cnt]=v;nex[cnt]=head[u];head[u]=cnt;}

int dep[maxn],dad[maxn],siz[maxn],son[maxn],chain[maxn],dfn[maxn];//
void dfs1(int u,int father){//dfs1(1,0)
dep[u]=dep[father]+1;//ini because dep[0]=1
dad[u]=father, siz[u]=1, son[u]=-1;
for(int i=head[u];~i;i=nex[i]){
int v=to[i];
if(v==father)continue;
dfs1(v,u);
siz[u]+=siz[v];
if(son[u]==-1||siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int s,int&step){
dfn[u]=++step;
chain[u]=s;
if(son[u]!=-1) dfs2(son[u],s,step);
for(int i=head[u];~i;i=nex[i]){
int v=to[i];
if(v!=son[u]&&v!=dad[u]) dfs2(v,v,step);
}
}
int query(int x,int y,int k){
int res=0;
while(chain[x]!=chain[y]){
if(dep[chain[x]]<dep[chain[y]]) swap(x,y); //dep[chain[x]]>dep[chain[y]]
res+=segtree::query(dfn[chain[x]],dfn[x],k);// [左,右,值]
x=dad[chain[x]];
}
if(dep[x]>dep[y]) swap(x,y);// dep[x]<dep[y]
return res+segtree::query(dfn[x],dfn[y],k);// [左,右,值]
}