zoukankan      html  css  js  c++  java
  • Link Cut Tree

    前方高能

    这里面的任何一道题目都有可能汲取完你半天的时间(平均)

    模板:

    //splay
    struct node{
    	int ...,fa,son[2];
    	bool z;
    }t[maxn];
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void reverse(int p){if(p==0)return;swap(t[p].son[0],t[p].son[1]);t[p].z^=1;}
    void pushup(int p){...}
    void pushdown(int p){
        if(t[p].z){
            reverse(t[p].son[0]);
            reverse(t[p].son[1]);
            t[p].z=0;
        }
    }
    void pushall(int p){if(!isroot(p))pushall(t[p].fa);pushdown(p);}
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s,t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q);
    	pushup(p);
    }
    void splay(int p){
    	pushall(p);
    	while(!isroot(p)){
    		int q=t[p].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    //lct
    void access(int x){
    	for(int y=0;x;y=x,x=t[x].fa){
    		splay(x);
    		t[x].son[1]=y;
    		pushup(x);
    	}
    }
    void change(int x){access(x),splay(x);...}
    int query(int x){access(x),splay(x);return ...;}
    

    (O(nlog n))
    以下默认(n,mle 10^5)

    Part 1

    A.

    1. 修改边权
    2. 询问路径上最大边权

    首先把边权转化为点权。两种方法:

    1. 新加 (n-1) 个点代表边,点权为对应的边权;

    2. 把边权赋到深度较深的端点的点权上。

    3. access+splay+修改+pushup

    4. makeroot+splay+询问

    B.

    1. 询问 (a)(b) 的距离
    2. 询问 (a)(b) 的单向路径上的第 (k) 个点

    1. access(b)+splay(b)+return t[ch[b][0]].sz
    2. access(b)+splay(b)+splay的kth操作

    C.

    每个节点有黑白两种颜色。

    1. 改变节点的颜色
    2. 询问 (a)(b) 的单向路径上的第一个黑色的节点

    1. access+splay+修改
    2. 同splay求前驱操作

    D.

    有边权

    1. 改变节点颜色
    2. 求树上的最远点对

    边权(→)点权
    本题与上面3题不同,需要维护子树信息。
    方法是,对于每一个节点开一个multiset,维护节点所有虚儿子的信息。
    本题的难点在pushup函数。
    维护四个值 (mx,lmx,rmx,sum)(mx) 表示当前子树中最远点对距离, (lmx) 表示当前子树中深度最浅的点的最远点对距离, (rmx) 表示当前子树中深度最深的点的最远点对距离, (sum) 表示子树中点权和。

    Code

    #include<bits/stdc++.h>
    #define maxn 100003
    #define INF 1050000000
    using namespace std;
    struct edge{int to,next,w;}e[maxn<<1];
    int head[maxn],cnte;
    void add(int u,int v,int w){e[++cnte].to=v,e[cnte].w=w,e[cnte].next=head[u],head[u]=cnte;}
    int n,val[maxn],a[maxn];
    
    struct node{
    	int mx,lmx,rmx,sum,fa,son[2];
    }t[maxn];
    multiset<int> light_lmx[maxn],light_mx[maxn];
    int first(const multiset<int>& st){return st.empty()?-INF:*--st.end();}
    int second(const multiset<int>& st){return st.size()<=1?-INF:*----st.end();}
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void pushup(int p){
    	int L=t[p].son[0],R=t[p].son[1],syk=max(val[p]?-INF:0,first(light_lmx[p])),
    	sykl=max(syk,t[L].rmx+a[p]),sykr=max(syk,t[R].lmx);
    	t[p].lmx=max(t[L].lmx,t[L].sum+a[p]+sykr);
    	t[p].rmx=max(t[R].rmx,t[R].sum+sykl);
    	t[p].mx=max(max(max(max(max(max(t[L].mx,t[R].mx),t[L].rmx+a[p]+sykr),t[R].lmx+sykl),first(light_mx[p])),
    	first(light_lmx[p])+second(light_lmx[p])),val[p]?-INF:max(first(light_lmx[p]),0));
    	t[p].sum=t[L].sum+t[R].sum+a[p];
    }
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s,t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q);
    	pushup(p);
    }
    void splay(int p){
    	while(!isroot(p)){
    		int q=t[p].fa,r=t[q].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    void access(int x){
    	for(int y=0;x;y=x,x=t[x].fa){
    		splay(x);
    		if(t[x].son[1])light_lmx[x].insert(t[t[x].son[1]].lmx),light_mx[x].insert(t[t[x].son[1]].mx);
    		t[x].son[1]=y;
    		if(y)light_lmx[x].erase(light_lmx[x].find(t[y].lmx)),light_mx[x].erase(light_mx[x].find(t[y].mx));
    		pushup(x);
    	}
    }
    void change(int x){
    	access(x),splay(x);
    	val[x]^=1;
    	pushup(x);
    }
    int query(){
    	splay(1);
    	return t[1].mx;
    }
    
    void dfs(int u,int last){
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(v==last)continue;
    		t[v].fa=u;
    		a[v]=e[i].w;
    		dfs(v,u);
    		light_mx[u].insert(t[v].mx);
    		light_lmx[u].insert(t[v].lmx);
    	}
    	pushup(u);
    }
    void init(){for(int i=0;i<=n;i++)t[i].mx=t[i].lmx=t[i].rmx=-INF;}
    char mo[2];
    int main(){
    	scanf("%d",&n);
    	init();
    	for(int i=1;i<n;i++){
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		add(u,v,w);
    		add(v,u,w);
    	}
    	dfs(1,0);
    	int Q;
    	scanf("%d",&Q);
    	while(Q--){
    		scanf("%s",mo);
    		if(*mo=='C'){
    			int x;
    			scanf("%d",&x);
    			change(x);
    		}
    		else{
    			int tmp=query();
    			if(tmp<0)puts("They have disappeared.");
    			else printf("%d
    ",tmp);
    		}
    	}
    	return 0;
    }
    

    E.

    1. 改变节点颜色
    2. 询问与节点 (v) 最近的节点

    本题是D题的弱化版
    对于每个节点维护三个值 (sz,lmx,rmx)(sz) 指当前子树的大小,其他两个与上题意义相同。

    Code

    #include<bits/stdc++.h>
    #define maxn 100003
    #define INF 1050000000
    using namespace std;
    struct edge{int to,next;}e[maxn<<1];
    int head[maxn],cnte;
    void add(int u,int v){e[++cnte].to=v,e[cnte].next=head[u],head[u]=cnte;}
    int n,val[maxn];
    
    struct node{
    	int lmi,rmi,sz,fa,son[2];
    }t[maxn]; 
    multiset<int> light_lmi[maxn];
    int first(const multiset<int>& st){return st.empty()?INF:*st.begin();}
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void pushup(int p){
    	int L=t[p].son[0],R=t[p].son[1],syk=min(val[p]?INF:0,first(light_lmi[p])+1);
    	t[p].lmi=min(t[L].lmi,t[L].sz+min(syk,t[R].lmi+1));
    	t[p].rmi=min(t[R].rmi,t[R].sz+min(syk,t[L].rmi+1));
    	t[p].sz=t[L].sz+t[R].sz+1;
    }
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s,t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q);
    	pushup(p);
    }
    void splay(int p){
    	while(!isroot(p)){
    		int q=t[p].fa,r=t[q].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    void access(int x){
    	for(int y=0;x;y=x,x=t[x].fa){
    		splay(x);
    		if(t[x].son[1])light_lmi[x].insert(t[t[x].son[1]].lmi);
    		t[x].son[1]=y;
    		if(y)light_lmi[x].erase(light_lmi[x].find(t[y].lmi));
    		pushup(x);
    	}
    }
    void change(int x){
    	access(x),splay(x);
    	val[x]^=1;
    	pushup(x);
    }
    int query(int x){
    	access(x),splay(x);
    	return t[x].rmi>=n?-1:t[x].rmi;
    }
    
    void dfs(int u,int last){
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(v==last)continue;
    		t[v].fa=u;
    		dfs(v,u);
    		light_lmi[u].insert(t[v].lmi);
    	}
    	pushup(u);
    }
    void init(){for(int i=0;i<=n;i++)t[i].lmi=t[i].rmi=INF,val[i]=1;}
    int main(){
    	scanf("%d",&n);
    	init();
    	for(int i=1;i<n;i++){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add(u,v);
    		add(v,u);
    	}
    	dfs(1,0);
    	int Q;
    	scanf("%d",&Q);
    	while(Q--){
    		int mo,x;
    		scanf("%d%d",&mo,&x);
    		if(mo){
    			printf("%d
    ",query(x));
    		}
    		else{
    			change(x);
    		}
    	}
    	return 0;
    }
    

    F.

    1. 改变节点颜色
    2. 把颜色相同且相邻的节点看成一个连通块,询问某节点所在连通块大小。

    解 1

    运用link和cut函数,每次更改节点后大力link、cut更改节点的邻边。
    但是会被菊花图给卡掉

    解 2

    点→边
    建黑、白两颗LCT,如果当前节点为白,则在白LCT中与它的父亲连边,否则在黑LCT中。
    这样能保证时间复杂度正确。
    注意判断一个连通块的根节点是否与该连通块颜色相同。

    Code

    #include<bits/stdc++.h>
    #define maxn 100003
    using namespace std;
    template<typename tp>
    void read(tp& x){
    	x=0;
    	char c=getchar();
    	bool sgn=0;
    	while((c<'0'||c>'9')&&c!='-')c=getchar();
    	if(c=='-')sgn=1,c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    	if(sgn)x=-x;
    }
    template<typename tp>
    void write(tp x){
    	if(x<0)putchar('-'),write(-x);
    	else{
    		if(x>=10)write(x/10);
    		putchar(x%10+'0');
    	}
    }
    
    struct edge{int to,next;}e[maxn<<1];
    int head[maxn],cnte;
    void add(int u,int v){e[++cnte].to=v,e[cnte].next=head[u],head[u]=cnte;}
    int n,fa[maxn],val[maxn];
    
    struct LCT{
    	struct node{
    		int sum,light,fa,son[2];
    	}t[maxn];
    	bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    	bool chk(int p){return t[t[p].fa].son[1]==p;}
    	void pushup(int p){
    		t[p].sum=t[t[p].son[0]].sum+t[t[p].son[1]].sum+t[p].light+1;
    	}
    	void rotate(int p){
    		int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    		t[q].son[k]=s,t[s].fa=q;
    		if(!isroot(q))t[r].son[chk(q)]=p;
    		t[p].fa=r;
    		t[p].son[!k]=q,t[q].fa=p;
    		pushup(q);
    		pushup(p);
    	}
    	void splay(int p){
    		while(!isroot(p)){
    			int q=t[p].fa,r=t[q].fa;
    			if(!isroot(q)){
    				if(chk(p)==chk(q))rotate(q);
    				else rotate(p);
    			}
    			rotate(p);
    		}
    	}
    	int find(int p){
    		while(t[p].son[0])p=t[p].son[0];
    		splay(p);
    		return p;
    	}
    
    	void access(int x){
    		for(int y=0;x;y=x,x=t[x].fa){
    			splay(x);
    			t[x].light+=t[t[x].son[1]].sum;
    			t[x].son[1]=y;
    			t[x].light-=t[t[x].son[1]].sum;
    			pushup(x);
    		}
    	}
    	void link(int x){
    		if(fa[x]==0)return;
    		access(fa[x]),splay(fa[x]),splay(x);
    		t[x].fa=fa[x];
    		t[fa[x]].light+=t[x].sum;
    		pushup(fa[x]);
    	}
    	void cut(int x){
    		if(fa[x]==0)return;
    		access(x),splay(x);
    		t[x].son[0]=t[t[x].son[0]].fa=0;
    		pushup(x);
    	}
    	int query(int x){
    		access(x),splay(x);
    		int tmp=find(x);
    		return val[tmp]==val[x]?t[tmp].sum:t[t[tmp].son[1]].sum;
    	}
    
    	void init(int n){for(int i=1;i<=n;i++)t[i].sum=1,t[i].light=t[i].fa=t[i].son[0]=t[i].son[1]=0;}
    }tree[2];
    
    void dfs(int u,int last){
    	fa[u]=last;
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(v==last)continue;
    		dfs(v,u);
    	}
    }
    
    int main(){
    	read(n);
    	tree[0].init(n);
    	tree[1].init(n);
    	for(int i=1;i<n;i++){
    		int u,v;
    		read(u),read(v);
    		add(u,v),add(v,u);
    	}
    	dfs(1,0);
    	for(int i=1;i<=n;i++)tree[0].link(i);
    	int Q;
    	read(Q);
    	while(Q--){
    		int mo,x;
    		read(mo),read(x);
    		if(mo){
    			tree[val[x]].cut(x);
    			val[x]^=1;
    			tree[val[x]].link(x);
    		}
    		else{
    			write(tree[val[x]].query(x)),putchar('
    ');
    		}
    	}
    	return 0;
    }
    

    G.

    有点权

    1. 询问连通块中的最大点权和
    2. 改变节点颜色
    3. 修改点权

    综合 (C,F) 两题思想,在 (F) 题基础上多维护一个 (mx)

    Code

    #include<bits/stdc++.h>
    #define maxn 100003
    #define INF 1050000000
    using namespace std;
    template<typename tp>
    void read(tp& x){
    	x=0;
    	char c=getchar();
    	bool sgn=0;
    	while((c<'0'||c>'9')&&c!='-')c=getchar();
    	if(c=='-')sgn=1,c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    	if(sgn)x=-x;
    }
    template<typename tp>
    void write(tp x){
    	if(x<0)putchar('-'),write(-x);
    	else{
    		if(x>=10)write(x/10);
    		putchar(x%10+'0');
    	}
    }
    
    struct edge{int to,next;}e[maxn<<1];
    int head[maxn],cnte;
    void add(int u,int v){e[++cnte].to=v,e[cnte].next=head[u],head[u]=cnte;}
    int n,fa[maxn],val[maxn],a[maxn];
    
    struct LCT{
    	struct node{
    		int mx,fa,son[2];
    	};node t[maxn];
    	multiset<int,greater<int> > light[maxn];
    	bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    	bool chk(int p){return t[t[p].fa].son[1]==p;}
    	void pushup(int p){
    		t[p].mx=max(max(max(t[t[p].son[0]].mx,t[t[p].son[1]].mx),a[p]),light[p].empty()?-INF:*light[p].begin());
    	}
    	void rotate(int p){
    		int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    		t[q].son[k]=s,t[s].fa=q;
    		if(!isroot(q))t[r].son[chk(q)]=p;
    		t[p].fa=r;
    		t[p].son[!k]=q,t[q].fa=p;
    		pushup(q);
    		pushup(p);
    	}
    	void splay(int p){
    		while(!isroot(p)){
    			int q=t[p].fa,r=t[q].fa;
    			if(!isroot(q)){
    				if(chk(p)==chk(q))rotate(q);
    				else rotate(p);
    			}
    			rotate(p);
    		}
    	}
    	int find(int p){
    		while(t[p].son[0])p=t[p].son[0];
    		splay(p);
    		return p;
    	}
    
    	void access(int x){
    		for(int y=0;x;y=x,x=t[x].fa){
    			splay(x);
    			light[x].insert(t[t[x].son[1]].mx);
    			t[x].son[1]=y;
    			light[x].erase(light[x].lower_bound(t[t[x].son[1]].mx));
    			pushup(x);
    		}
    	}
    	void link(int x){
    		if(fa[x]==0)return;
    		access(fa[x]),splay(fa[x]),splay(x);
    		t[x].fa=fa[x];
    		light[fa[x]].insert(t[x].mx);
    		pushup(fa[x]);
    	}
    	void cut(int x){
    		if(fa[x]==0)return;
    		access(x),splay(x);
    		t[x].son[0]=t[t[x].son[0]].fa=0;
    		pushup(x);
    	}
    	int query(int x){
    		access(x),splay(x);
    		int tmp=find(x);
    		return val[tmp]==val[x]?t[tmp].mx:t[t[tmp].son[1]].mx;
    	}
    
    	void init(int n){for(int i=0;i<=n;i++)t[i].mx=-INF,t[i].fa=t[i].son[0]=t[i].son[1]=0;}
    }tree[2];
    
    void dfs(int u,int last){
    	fa[u]=last;
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(v==last)continue;
    		dfs(v,u);
    	}
    }
    
    int main(){
    	read(n);
    	tree[0].init(n);
    	tree[1].init(n);
    	for(int i=0;i<=n;i++)a[i]=-INF;
    	for(int i=1;i<n;i++){
    		int u,v;
    		read(u),read(v);
    		add(u,v),add(v,u);
    	}
    	for(int i=1;i<=n;i++)read(val[i]);
    	for(int i=1;i<=n;i++)read(a[i]),tree[0].t[i].mx=tree[1].t[i].mx=a[i];
    	dfs(1,0);
    	for(int i=1;i<=n;i++)tree[val[i]].link(i);
    	int Q;
    	read(Q);
    	while(Q--){
    		int mo,x;
    		read(mo),read(x);
    		if(mo==1){
    			tree[val[x]].cut(x);
    			val[x]^=1;
    			tree[val[x]].link(x);
    		}
    		else if(mo==2){
    			int y;
    			read(y);
    			tree[0].access(x),tree[0].splay(x),tree[1].access(x),tree[1].splay(x);
    			a[x]=y;
    			tree[0].pushup(x),tree[1].pushup(x);
    		}
    		else{
    			write(tree[val[x]].query(x)),putchar('
    ');
    		}
    	}
    	return 0;
    }
    

    Part 2

    A

    (N) 个点 (M) 条边的无向图,询问保留图中编号在 ([l,r]) 的边的时候图中的联通块个数。

    考虑以下结论:

    一个无向图中连通块的个数,等于它的顶点数-它的所有连通分量的生成树的边数和。

    考虑离线做法。先将 (r) 排序,同时用树状数组维护 (1-i) 的边中当前已经扫到了哪些边。

    Code

    #include<bits/stdc++.h>
    #define maxn 400003
    #define INF 1050000000
    #define o(a) printf(#a": ");for(int j=1;j<=n+m;j++)printf("%d ",a);puts("");
    using namespace std;
    struct edge{int from,to;}e[maxn];
    int n,m,f[maxn],ans[maxn];
    int find(int x){return x!=f[x]?f[x]=find(f[x]):f[x];}
    struct QQ{int l,r,num;bool operator <(const QQ& x)const{return r<x.r;}}q[maxn];
    
    struct node{int mi,fa,son[2];bool z;}t[maxn];
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void reverse(int p){if(p==0)return;swap(t[p].son[0],t[p].son[1]);t[p].z^=1;}
    void pushup(int p){t[p].mi=min(min(t[t[p].son[0]].mi,t[t[p].son[1]].mi),p<=n?INF:p);}
    void pushdown(int p){
    	if(t[p].z){
    		reverse(t[p].son[0]);
    		reverse(t[p].son[1]);
    		t[p].z=0;
    	}
    }
    void pushall(int p){if(!isroot(p))pushall(t[p].fa);pushdown(p);}
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s;
    	if(s)t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q),pushup(p);
    }
    void splay(int p){
    	pushall(p);
    	while(!isroot(p)){
    		int q=t[p].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    
    void access(int x){for(int y=0;x;y=x,x=t[x].fa)splay(x),t[x].son[1]=y,pushup(x);}
    void makeroot(int x){access(x),splay(x),reverse(x);}
    void link(int x,int y){
    	makeroot(x);
    	t[x].fa=y;
    }
    void cut(int x,int y){
    	makeroot(x),access(y),splay(y);
    	t[x].fa=t[y].son[0]=0;
    	pushup(y);
    }
    int query(int x,int y){makeroot(x),access(y),splay(y);return t[y].mi;}
    
    int TREE[maxn];
    void ADD(int pos,int k){while(pos<=n+m)TREE[pos]+=k,pos+=pos&-pos;}
    int QUERY(int pos){int ret=0;while(pos)ret+=TREE[pos],pos-=pos&-pos;return ret;}
    
    int main(){
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		int Q;
    		scanf("%d%d%d",&n,&m,&Q);
    		for(int i=n+1;i<=n+m;i++)scanf("%d%d",&e[i].from,&e[i].to);
    		for(int i=1;i<=Q;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].l+=n,q[i].r+=n,q[i].num=i;
    		sort(q+1,q+Q+1);
    		for(int i=1;i<=n;i++)f[i]=i;
    		for(int i=0;i<=n+m;i++)t[i].mi=t[i].fa=t[i].son[0]=t[i].son[1]=t[i].z=TREE[i]=0;
    		for(int i=n+1;i<=n+m;i++)t[i].mi=i;
    		t[0].mi=INF;
    		for(int i=n+1,j=1;i<=n+m;i++){
    			int u=e[i].from,v=e[i].to,fu=find(u),fv=find(v);
    // printf("u:%d i:%d v:%d
    ",u,i,v);
    			if(u!=v){
    				if(fu==fv){
    					int p=query(u,v);
    					cut(e[p].from,p),cut(p,e[p].to);
    // printf("# u:%d p:%d v:%d
    ",e[p].from,p,e[p].to);
    					ADD(p,-1);
    				}
    				else{
    					f[fu]=fv;
    				}
    				link(u,i),link(i,v);
    				ADD(i,1);
    			}
    			for(;j<=Q&&q[j].r<=i;j++){
    				ans[q[j].num]=n-QUERY(q[j].r)+QUERY(q[j].l-1);
    // printf("q[j].num:%d ans:%d
    ",q[j].num,ans[q[j].num]);
    			}
    // o(t[j].fa);
    		}
    		for(int i=1;i<=Q;i++)printf("%d
    ",ans[i]);
    	}
    	return 0;
    }
    

    E

    同A,但强制在线。

    把树状数组升级为主席树即可。

    Code

    #include<bits/stdc++.h>
    #define maxn 400003
    #define INF 1050000000
    #define o(a) printf(#a": ");for(int j=1;j<=n+m;j++)printf("%d ",a);puts("");
    using namespace std;
    namespace FASTIO{
        static const int MAXN=10000000;
        char gc(){
            static char In[MAXN],*at=In,*en=In;
            if(at==en)en=(at=In)+fread(In,1,MAXN,stdin);
            return at==en?EOF:*at++;
        }
        template<class tp>
        void read(tp& x){
            x=0;
            char c=gc();
            bool sgn=0;
            while((c<'0'||c>'9')&&c!='-'&&c!=EOF)c=gc();
            if(c==EOF)return;
            if(c=='-')sgn=1,c=gc();
            while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=gc();
            if(sgn)x=-x;
        }
        void read(char* str){
            char c=gc();
            while((c==' '||c=='
    '||c=='
    '||c=='	')&&c!=EOF)c=gc();
            if(c!=EOF)while(c!=' '&&c!='
    '&&c!='
    '&&c!='	')*str++=c,c=gc();
            *str=0;
        }
        char _In[MAXN],*_at=_In;
        void pc(char c){
            if(_at==_In+MAXN)fwrite(_at=_In,1,MAXN,stdout);
            *_at++=c;
        }
        template<typename tp>
        void write(tp x){
            if(x<0)pc('-'),write(-x);
            else{
                if(x>=10)write(x/10);
                pc(x%10+'0');
            }
        }
        void flush(){fwrite(_In,1,_at-_In,stdout),_at=_In;}
    }using FASTIO::gc;using FASTIO::pc;using FASTIO::read;using FASTIO::write;using FASTIO::flush;
    
    struct edge{int from,to;}e[maxn];
    int n,m,f[maxn];
    int find(int x){return x!=f[x]?f[x]=find(f[x]):f[x];}
    
    struct node{int mi,fa,son[2];bool z;}t[maxn];
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void reverse(int p){if(p==0)return;swap(t[p].son[0],t[p].son[1]);t[p].z^=1;}
    void pushup(int p){t[p].mi=min(min(t[t[p].son[0]].mi,t[t[p].son[1]].mi),p<=n?INF:p);}
    void pushdown(int p){
    	if(t[p].z){
    		reverse(t[p].son[0]);
    		reverse(t[p].son[1]);
    		t[p].z=0;
    	}
    }
    void pushall(int p){if(!isroot(p))pushall(t[p].fa);pushdown(p);}
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s;
    	if(s)t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q),pushup(p);
    }
    void splay(int p){
    	pushall(p);
    	while(!isroot(p)){
    		int q=t[p].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    
    void access(int x){for(int y=0;x;y=x,x=t[x].fa)splay(x),t[x].son[1]=y,pushup(x);}
    void makeroot(int x){access(x),splay(x),reverse(x);}
    void link(int x,int y){
    	makeroot(x);
    	t[x].fa=y;
    }
    void cut(int x,int y){
    	makeroot(x),access(y),splay(y);
    	t[x].fa=t[y].son[0]=0;
    	pushup(y);
    }
    int query(int x,int y){makeroot(x),access(y),splay(y);return t[y].mi;}
    
    struct NODE{int sum,son[2];}TREE[maxn*40];
    int CNT,ROOT[maxn];
    void ADD(int p,int& q,int l,int r,int pos,int k){
    	q=++CNT;
    	TREE[q].sum=TREE[p].sum+k;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(pos<=mid){
    		TREE[q].son[1]=TREE[p].son[1];
    		ADD(TREE[p].son[0],TREE[q].son[0],l,mid,pos,k);
    	}
    	else{
    		TREE[q].son[0]=TREE[p].son[0];
    		ADD(TREE[p].son[1],TREE[q].son[1],mid+1,r,pos,k);
    	}
    }
    int QUERY(int p,int l,int r,int seg_l,int seg_r){
    	if(seg_l<=l&&r<=seg_r)return TREE[p].sum;
    	int mid=(l+r)>>1,ret=0;
    	if(seg_l<=mid)ret+=QUERY(TREE[p].son[0],l,mid,seg_l,seg_r);
    	if(seg_r>mid)ret+=QUERY(TREE[p].son[1],mid+1,r,seg_l,seg_r);
    	return ret;
    }
    
    int main(){
    	int Q,ty;
    	read(n),read(m),read(Q),read(ty);
    	for(int i=n+1;i<=n+m;i++)read(e[i].from),read(e[i].to);
    	for(int i=1;i<=n;i++)f[i]=i;
    	for(int i=n+1;i<=n+m;i++)t[i].mi=i;
    	t[0].mi=INF;
    	for(int i=n+1,j=1;i<=n+m;i++){
    		int u=e[i].from,v=e[i].to,fu=find(u),fv=find(v);
    // printf("u:%d i:%d v:%d
    ",u,i,v);
    		ROOT[i]=ROOT[i-1];
    		if(u!=v){
    			if(fu==fv){
    				int p=query(u,v);
    				cut(e[p].from,p),cut(p,e[p].to);
    // printf("# u:%d p:%d v:%d
    ",e[p].from,p,e[p].to);
    				ADD(ROOT[i],ROOT[i],n+1,n+m,p,-1);
    			}
    			else{
    				f[fu]=fv;
    			}
    			link(u,i),link(i,v);
    			ADD(ROOT[i],ROOT[i],n+1,n+m,i,1);
    		}
    // o(t[j].fa);
    	}
    	int last=0;
    	while(Q--){
    		int l,r;
    		read(l),read(r);
    		if(ty)l^=last,r^=last;
    		last=n-QUERY(ROOT[r+n],n+1,n+m,l+n,r+n);
    		write(last),pc('
    ');
    	}
    	flush();
    	return 0;
    }
    

    B ZJOI2018 历史

    给出一棵树,给定每一个点的access次数,计算轻重链切换次数的最大值,带修改。

    10 pts

    ( ext{dfs})

    30 pts(不带修改)

    考虑树形dp。
    假设现在考虑到节点 (u) ,把 (u) 的每一棵子树中所有节点看成同一种颜色,每个 (u) 的子节点(记为 (v) )的答案为 (sum[v])(a[u])(u;access) 的次数。
    (s=sum sum[v]+a[u],t=max{sum[v]}) ,那么:

    [sum[u]=min(s-1,2*(s-t)) ]

    举例说明:

    1. 第一种情况
      颜色 (A×3,B×4,C×5)
      一组可行解是 (CACBCACBCBABCACBCACBCBAB)
      答案是 (11=12-1)

    2. 第二种情况
      颜色 (A×2,B×3,C×7)
      一组可行解是 (CACBCACBCBCCCACBCACBCBCC)
      答案是 (10=2×(12-7))

    100 pts

    考虑如何修改。
    我们看到 (sum[u]=2*(s-t)) 的条件是 (2*t>s+1)
    然后发现这样的子树 (v)(u) 的所有子树中最多只有1个
    于是考虑树剖或LCT,如果某个 (v) 使得 (2*t>s+1) ,则 ((u,v)) 为实(重)边,否则为虚(轻)边。下面我们讨论LCT做法。
    (pushup) 维护两个数组 (sum,light)(sum) 意义如上, (light) 维护虚边信息。
    把修改嵌在access过程里,用以上的东西判断是否需要切换虚实边,同时更新各个数组。修改时需要同时更新答案。

    Code

    #include<bits/stdc++.h>
    #define maxn 400003
    #define o(a) printf(#a": ");for(int i=0;i<=n;i++)printf("%d ",a[i]);puts("");
    using namespace std;
    struct edge{int to,next;}e[maxn<<1];
    int head[maxn],cnte;
    void add(int u,int v){e[++cnte].to=v,e[cnte].next=head[u],head[u]=cnte;}
    int n,tp[maxn];
    long long sum[maxn],a[maxn],light[maxn],ans;
    
    struct node{int fa,son[2];}t[maxn];
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void pushup(int p){sum[p]=sum[t[p].son[0]]+sum[t[p].son[1]]+light[p]+a[p];}
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s,t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q);
    	pushup(p);
    }
    void splay(int p){
    	while(!isroot(p)){
    		int q=t[p].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    
    void init(int u){
    	int mxi=u;
    	long long mx=a[u];
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(v==t[u].fa)continue;
    		t[v].fa=u;
    		init(v);
    		light[u]+=sum[v];
    		if(sum[v]>mx)mx=sum[v],mxi=v;
    	}
    	sum[u]=light[u]+a[u];
    	if(mx*2>=sum[u]+1){
    		ans+=2*(sum[u]-mx);
    		if(u==mxi)tp[u]=1;
    		else tp[u]=2,t[u].son[1]=mxi,light[u]-=sum[mxi];
    	}
    	else{
    		tp[u]=0,ans+=sum[u]-1;
    	}
    }
    void change(int x,long long k){
    	for(int y=0;x;y=x,x=t[x].fa){
    		splay(x);
    		int &L=t[x].son[0],&R=t[x].son[1];
    		long long s=sum[x]-sum[L];
    		if(tp[x]==0)ans-=(s-1);
    		if(tp[x]==1)ans-=2*(s-a[x]);
    		if(tp[x]==2)ans-=2*(s-sum[R]);
    		s+=k,sum[x]+=k;
    		if(y==0)a[x]+=k;
    		else light[x]+=k;
    		if(sum[y]*2>=s+1)light[x]+=sum[R],R=y,light[x]-=sum[R];
    		if(sum[R]*2>=s+1)tp[x]=2,ans+=2*(s-sum[R]);
    		else{
    			if(R)light[x]+=sum[R],R=0;
    			if(a[x]*2>=s+1)tp[x]=1,ans+=2*(s-a[x]);
    			else tp[x]=0,ans+=s-1,R=0;
    		}
    	}
    }
    
    int main(){
    	int Q;
    	scanf("%d%d",&n,&Q);
    	for(int i=1;i<=n;i++)scanf("%lld",a+i);
    	for(int i=1;i<n;i++){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add(u,v),add(v,u);
    	}
    	init(1);
    	printf("%lld
    ",ans);
    	while(Q--){
    		int x;
    		long long k;
    		scanf("%d%lld",&x,&k);
    		change(x,k);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    C

    (N) 个未知数 (x[1..n])(N) 个等式组成的同余方程组:
    (x[i]=k[i]*x[p[i]]+b[i]\% 10007)
    其中, (k[i],b[i],x[i]in [0,10007)∩Z)
    你要应付 (Q) 个事务,每个是两种情况之一:
    一、询问当前 (x[a]) 的解
    A a
    无解输出 (-1)
    (x[a]) 有多解输出 (-2)
    否则输出 (x[a])
    二、修改一个等式
    C a k[a] p[a] b[a]

    我们发现这是一个基环树森林,且每个节点只有一条出边。(连边 (u→P[u])
    用LCT维护基环树的一般方法是对根节点记一个special_fa,每个节点维护一个关于 (P[rt]) 的一次函数。
    于是在连边、断边时大力分类讨论。
    对于询问,你通过一堆access+splay,能够得到两个方程,(设 (u) 为询问的节点, (rt)(u) 所在树的根)形如 (u=k_1*P[rt]+b_1)(P[rt]=k_2*P[rt]+b_2)

    1. (k_2=1;and;b_2!=0) ,无解
    2. (k_2=1;and;b_2=0) ,无穷解
    3. (k_2!=1) ,用exgcd求解(注意特判 (k_2=0) 的情况)

    Code

    #include<bits/stdc++.h>
    #define maxn 30003
    #define mod 10007
    #define LINE puts("----------------------------------------------------------")
    #define O(a) printf(#a": ");for(int i=1;i<=n;i++)printf("%d ",a);puts("");
    using namespace std;
    int n,Q,inv[maxn];
    bool vis[maxn],instk[maxn];
    int Plus(int x,int y){return (x+=y)>=mod?x-mod:x;}
    int mul(int x,int y){return x*y%mod;}
    int Div(int x,int y){return x*inv[y]%mod;}
    int egcd(int a,int b,int& x,int& y){
        if(b==0){
            x=1;
            y=0;
            return a;
        }
        int ans=egcd(b,a%b,x,y);
        int tmp=x;
        x=y;
        y=tmp-a/b*y;
        return ans;
    }
    int cal(int a,int b,int c){ //ax+by=c (gcd(a,b)==1)
        int x,y;
        egcd(a,b,x,y);
        x*=c;
        int ans=x%b;
        if(ans<=0)ans+=b;
        return ans;
    }
    struct T{int K,B;T():K(1),B(0){}}a[maxn];
    T operator +(const T& t2,const T& t3){T t1;t1.K=mul(t3.K,t2.K),t1.B=Plus(mul(t3.K,t2.B),t3.B);return t1;}
    
    struct node{T val;int sfa,fa,son[2];}t[maxn];
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void pushup(int p){
    	int L=t[p].son[0],R=t[p].son[1];
    	t[p].val=a[p];
    	if(L)t[p].val=t[L].val+t[p].val;
    	if(R)t[p].val=t[p].val+t[R].val;
    }
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s;
    	if(s)t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q),pushup(p);
    }
    void splay(int p){
    	while(!isroot(p)){
    		int q=t[p].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    void access(int x){for(int y=0;x;y=x,x=t[x].fa)splay(x),t[x].son[1]=y,pushup(x);}
    int findroot(int x){access(x),splay(x);while(t[x].son[0])x=t[x].son[0];return x;}
    bool oncyc(int x){
    	int rt=t[findroot(x)].sfa;
    	if(x==rt)return 1;
    	access(rt),splay(rt),splay(x);
    	return !isroot(rt);
    }
    void link(int x,int y){ //dep[x]>dep[y]
    	access(x),splay(x);
    	t[x].fa=y;
    }
    void cut(int x){ //dep[x]>dep[y]
    	access(x),splay(x);
    	t[x].son[0]=t[t[x].son[0]].fa=0;
    	pushup(x);
    }
    void change(int i,int k,int p,int b){
    	if(findroot(i)==i){
    		t[i].sfa=0;
    	}
    	else{
    		bool flag=oncyc(i);
    		int rt=findroot(i);
    		cut(i);
    		if(flag){
    			splay(rt);
    			t[rt].fa=t[rt].sfa;
    			t[rt].sfa=0;
    			pushup(t[rt].fa);
    		}
    	}
    	a[i].K=k,a[i].B=b;
    	pushup(i);
    	if(findroot(i)==findroot(p)){
    		t[i].sfa=p;
    	}
    	else{
    		link(i,p);
    	}
    }
    int query(int i){
    	access(i),splay(i);
    	T I=t[i].val;
    	int p=t[findroot(i)].sfa;
    	access(p),splay(p);
    	T P=t[p].val;
    	if(P.K==1)return P.B?-1:-2;
    	if(P.K==0)return Plus(mul(I.K,P.B),I.B);
    	return Plus(mul(I.K,mod+cal(P.K-1,mod,-P.B)),I.B);
    }
    
    void dfs(int u){
    	vis[u]=instk[u]=1;
    	if(vis[t[u].fa]){
    		if(instk[t[u].fa]){
    			t[u].sfa=t[u].fa;
    			t[u].fa=0;
    		}
    	}
    	else{
    		dfs(t[u].fa);
    	}
    	instk[u]=0;
    }
    
    int main(){
    	inv[1]=1;
    	for(int i=2;i<mod;i++)inv[i]=mul(mod-mod/i,inv[mod%i]);
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d%d",&a[i].K,&t[i].fa,&a[i].B);
    		a[i].K%=mod,a[i].B%=mod;
    		t[i].val=a[i];
    	}
    	for(int i=1;i<=n;i++){
    		if(!vis[i])dfs(i);
    	}
    	scanf("%d",&Q);
    	while(Q--){
    		char mo[2];
    		scanf("%s",mo);
    		if(*mo=='A'){
    			int i;
    			scanf("%d",&i);
    			printf("%d
    ",query(i));
    		}
    		else{
    			int i,k,p,b;
    			scanf("%d%d%d%d",&i,&k,&p,&b);
    			k%=mod,b%=mod;
    			change(i,k,p,b);
    		}
    	}
    	return 0;
    }
    

    D NOI2014 魔法森林

    为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个 (N) 节点 (M) 条边的无向图,节点标号为 (1..N) ,边标号为 (1..M) 。初始时小E同学在号节点 (1) ,隐士则住在号节点 (N) 。小E需要通过这一片魔法森林,才能够拜访到隐士。

    魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

    只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边 (E_i) 包含两个权值 (A_i)(B_i) 。若身上携带的A型守护精灵个数不少于 (A_i) ,且B型守护精灵个数不少于 (B_i) ,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。

    由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。

    考虑离线。
    从小到达枚举 (a) ,用LCT维护MST,对于当前枚举的边,如果边的两个端点已经连通,则找到路径上边权最大的一条边(必须大于当前枚举边权)替换掉

    Code

    #include<bits/stdc++.h>
    #define maxn 200003
    #define INF 1050000000
    #define o(a) printf(#a": ");for(int j=1;j<=n+m;j++)printf("%d ",a);puts("");
    using namespace std;
    
    struct edge{int from,to,a,b;bool operator <(const edge& x)const{return a<x.a;}}e[maxn];
    int n,m,f[maxn];
    int find(int x){return x!=f[x]?f[x]=find(f[x]):f[x];}
    
    struct node{int mxp,fa,son[2];bool z;}t[maxn];
    bool isroot(int p){return t[t[p].fa].son[0]!=p&&t[t[p].fa].son[1]!=p;}
    bool chk(int p){return t[t[p].fa].son[1]==p;}
    void reverse(int p){if(p==0)return;swap(t[p].son[0],t[p].son[1]);t[p].z^=1;}
    void pushup(int p){
    	int L=t[p].son[0],R=t[p].son[1];
    	t[p].mxp=p;
    	if(e[t[L].mxp].b>e[t[p].mxp].b)t[p].mxp=t[L].mxp;
    	if(e[t[R].mxp].b>e[t[p].mxp].b)t[p].mxp=t[R].mxp;
    }
    void pushdown(int p){
    	if(t[p].z){
    		reverse(t[p].son[0]);
    		reverse(t[p].son[1]);
    		t[p].z=0;
    	}
    }
    void pushall(int p){if(!isroot(p))pushall(t[p].fa);pushdown(p);}
    void rotate(int p){
    	int q=t[p].fa,r=t[q].fa,k=chk(p),s=t[p].son[!k];
    	t[q].son[k]=s;
    	if(s)t[s].fa=q;
    	if(!isroot(q))t[r].son[chk(q)]=p;
    	t[p].fa=r;
    	t[p].son[!k]=q,t[q].fa=p;
    	pushup(q);
    	pushup(p);
    }
    void splay(int p){
    	pushall(p);
    	while(!isroot(p)){
    		int q=t[p].fa;
    		if(!isroot(q)){
    			if(chk(p)==chk(q))rotate(q);
    			else rotate(p);
    		}
    		rotate(p);
    	}
    }
    
    void access(int x){for(int y=0;x;y=x,x=t[x].fa)splay(x),t[x].son[1]=y,pushup(x);}
    void makeroot(int x){access(x),splay(x),reverse(x);}
    void link(int x,int y){
    	makeroot(x);
    	t[x].fa=y;
    }
    void cut(int x,int y){
    	makeroot(x),access(y),splay(y);
    	t[x].fa=t[y].son[0]=0;
    	pushup(y);
    }
    int query(int x,int y){makeroot(x),access(y),splay(y);return t[y].mxp;}
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=n+1;i<=n+m;i++){
    		scanf("%d%d%d%d",&e[i].from,&e[i].to,&e[i].a,&e[i].b);
    	}
    	sort(e+n+1,e+n+m+1);
    	for(int i=1;i<=n;i++)f[i]=i;
    	for(int i=1;i<=n+m;i++)t[i].mxp=i;
    	int ans=INF;
    	for(int i=n+1;i<=n+m;i++){
    		int u=e[i].from,v=e[i].to,fu=find(u),fv=find(v);
    // printf("u:%d v:%d i:%d a:%d b:%d
    ",u,v,i,e[i].a,e[i].b);
    		if(fu==fv){
    			int p=query(u,v);
    // printf("# u:%d v:%d p:%d a:%d b:%d
    ",e[p].from,e[p].to,p,e[p].a,e[p].b);
    			if(e[p].b>e[i].b){
    				cut(e[p].from,p),cut(p,e[p].to);
    				link(u,i),link(i,v);
    			}
    		}
    		else{
    			f[fu]=fv;
    			link(u,i),link(i,v);
    		}
    		if(find(1)==find(n)){
    			ans=min(ans,e[i].a+e[query(1,n)].b);
    		}
    // o(t[j].fa);
    	}
    	printf("%d
    ",ans==INF?-1:ans);
    	return 0;
    }
    

    F

    $$color{white}{ ext{不做了,做了会死人的}}$$

  • 相关阅读:
    openfalcon源码分析之transfer
    openfalcon源码分析之hbs
    openfalcon源码分析之Judge
    kong插件官方文档翻译
    Lua 学习
    GO语言heap剖析及利用heap实现优先级队列
    GO语言list剖析
    算法之python创建链表实现cache
    杂项之rabbitmq
    杂项之python利用pycrypto实现RSA
  • 原文地址:https://www.cnblogs.com/BlogOfchc1234567890/p/10583196.html
Copyright © 2011-2022 走看看