• 浅谈树链剖分


    @

    什么是树链剖分?

    指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、SBT、SPLAY、线段树等)来维护每一条链,主要用来维护树上每条链的极值或和之类的。

    类似

    首先把树上倍增摆在前面,如果不会树上倍增就不必来看树链剖分。

    为什么要学树链剖分?

    有的人说:“我会树上倍增,我怕谁?”,没错,你怕的就是树链剖分。
    意思是:树上倍增能做的,树链剖分都能做;树链剖分能做的,树上倍增不一定能做。树上倍增虽然能求树上区间极值或和,但一旦有树上边或点权值修改的操作,您就死翘翘了~这时我们需要一个新的算法来补上我们的树上倍增操作的漏洞,没错,就是树链剖分!没错!你学了它一定不会后悔!!

    少废话,正题如下:

    首先,概念必须明白:

    轻儿子&重儿子

    对于一个节点,在它的所有儿子中,size(子树大小)最大的一个就是重儿子,其余的都是轻儿子。重儿子所在的树链叫做重链,其余叫做轻链

    这里写图片描述
    图中共有5条重链,分别是:
    1----2----6----3
    5
    3
    4----7
    8.

    我们需要维护什么?

    • tree数组,表示整棵树上的信息
    • tree[i].depth表示i的的深度
    • tree[i].top表示i所在的重链顶端的结点
    • tree[i].father表示i点的父亲
    • tree[i].key表示i点的权值
    • tree[i].To_tree表示节点i在线段树中的位置
    • tree[i].size表示以i为根的子树大小
    • tree[i].heavy_son表示i节点的重儿子
    • To_num[i]表示线段树中的i号节点对应的是原树中的位置
      //鉴于我是个蒟蒻,这里之讲用数据结构线段树维护

    怎么维护?

    第一次dfs,我们从根开始遍历整棵树,然后记录每个点的【depth,heavy_son,size,father,key】(dfs)。

    CODE:

    int dfs(int t)
    {
    	int y,mx=0;
    	tree[t].size=1;
    	bz[t]=true;
    	for (int i=last[t];i;i=next[i])
    	{
    		y=tov[i];
    		if(!tree[y].dep)
    		{
    			tree[y].depth=tree[t].depth+1;
    			tree[y].father=t;
    			tree[y].key=len[i];
    			dfs(y);
    			tree[t].size+=tree[y].size;
    			if(tree[y].size>mx)
    			{
    				mx=tree[y].size;
    				tree[t].heavy_son=y;
    			}
    		}
    	}
    }
    

    人工栈CODE:

    void biuld_tree()
    {
    	int i,x,y;
    	tot=1;
    	d[1].t=root;tree[1].depth=1;
    	for (i=1;i<=n;++i) cur[i]=last[i];
    	while(tot)
    	{
    		x=d[tot].t;
    		i=cur[x];
    		while(tree[tov[i]].depth) i=next[i];
    		if(!i)
    		{
    			d[tot--].maxx=0;
    			++tree[x].size;
    			tree[d[tot].t].size+=tree[x].size;
    			if(tree[x].size>d[tot].maxx) d[tot].maxx=tree[x].size,tree[d[tot].t].hs=x;
    			continue;
    		}
    		cur[x]=next[i];
    		y=tov[i];
    		tree[y].fath=x;
    		tree[y].depth=tree[x].depth+1;
    		num[y]=number[i];
    		d[++tot].t=y;		
    	} 		
    }
    

    操作:

    • 建树
    • 查询
      查询包括
      LCA查询
      区间答案查询

    建树

    建树的思想就是dfs,每次先搜索重儿子,保证重链上的每个点在线段树的位置是连续的。维护出每个点的【top,To_tree】以及To_num。

    CODE:

    void build_dfs(int t,int k)
    {
    	int y;
    	bz[t]=true;
    	tree[t].top=k;
    	tree[t].To_tree=++tot;
    	To_num[tot]=t;
    	if(tree[t].heavy_son) dfs2(tree[t].heavy_son,k);
    	for (int i=last[t];i;i=next[i])
    	{
    		y=tov[i];
    		if(y!=tree[t].father&&y!=tree[t].heavy_son) build_dfs(y,y);
    	}
    }
    

    人工栈CODE:

    void complete_tree()
    {
    	int i,x,y;
    	tot=1;sz=1;
    	d[1].t=root;d[1].topp=root;
    	tree[1].To_tree=1;tree[1].top=1;
    	bz[0]=1;to_num[1]=1;
    	for (i=1;i<=n;++i) cur[i]=last[i];
    	while(tot)
    	{
    		x=d[tot].t;
    		tree[x].top=d[tot].topp;
    		bz[x]=true;
    		if(!bz[tree[x].heavy_son])
    		{
    			tree[tree[x].heavy_son].top=d[tot].topp;
    			d[++tot].t=tree[x].heavy_son;
    			d[tot].topp=tree[tree[x].heavy_son].top;	
    			tree[tree[x].heavy_son].To_tree=++sz;
    			to_num[sz]=tree[x].heavy_son;
    			continue;			
    		}
    		i=cur[x];
    		while(bz[tov[i]]&&i) i=next[i];
    		if(!i)
    		{
    			d[tot--].topp=0;
    			continue;
    		}
    		cur[x]=next[i];
    		y=tov[i];
    		tree[tree[x].hs].top=d[tot].topp;
    		tree[y].To_tree=++sz;
    		to_num[sz]=y;
    		d[++tot].t=y;d[tot].topp=y;		
    	} 
    }
    

    查询

    LCA查询

    绝对比树上倍增的查询简单!
    我们查询x,y的LCA,使用树链剖分。
    STEPS

    1. 我们查看当前tree[x].top是否与tree[y].top相等
    2. 如果不相等,那么就将深度较大的(假设为x)跳到当前重链顶端的父亲。
    3. 一直这样迭代,直到它们top相等为止。
    4. 如果它们的top相等,有两种情况:
      ①x=y
      ②y是x的祖先(或x是y的祖先)

    那么LCA就是深度较小的那一个!
    是不是很简单~

    CODE:

    int query_LCA(int x,int y)
    {
    	while(tree[x].top!=tree[y].top)
    	{
    		if(tree[tree[x].top].depth>tree[tree[y].top].depth)	x=tree[tree[x].top].father;
    		else y=tree[tree[y].top].father;
    	}
    	return tree[x].depth<=tree[y].depth?x:y;
    }
    

    区间极值或和查询

    合理运用线段树等数据结构,在寻找LCA过程中,顺带处理区间极值或和的查询。

    CODE:

    int query_ans(int x,int y)
    {
    	int ans=INF;
    	while(tree[x].top!=tree[y].top)
    	{
    		if(tree[tree[x].top].depth>tree[tree[y].top].depth)	
    		{
    			ans=min(ans,find(1,n,1,tree[tree[x].top].To_tree,tree[x].To_tree));
    			x=tree[tree[x].top].father;
    		}
    		else
    		{
    			ans=min(ans,find(1,n,1,tree[tree[y].top].To_tree,tree[y].To_tree));
    			y=tree[tree[y].top].father;	
    		}
    	}
    	if(x==y) return ans;
    	if(tree[x].depth<tree[y].depth) ans=min(ans,find(1,n,1,tree[tree[x].heavy_son].To_tree,tree[y].To_tree));
    	else ans=min(ans,find(1,n,1,tree[tree[y].heavy_son].To_tree,tree[x].To_tree));
    	return ans;
    }
    

    本CODE为查询链上最小值

    基本知识讲完了,重在理解,你明白了吗?

    来道例题:

    【NOIP2013提高组day1】货车运输

    Time Limits: 1000 ms Memory Limits: 131072 KB Detailed Limits

    Description

    A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

    Input

    第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
    接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
    接下来一行有一个整数 q,表示有 q 辆货车需要运货。
    接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

    Output

    输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

    Sample Input

    4 3
    1 2 4
    2 3 3
    3 1 1
    3
    1 3
    1 4
    1 3

    Sample Output

    3
    -1
    3

    Data Constraint

    对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000;
    对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000;
    对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。

    给点时间思考~
    考虑~
    我们讲的是树链剖分,想想怎么才能和它挂钩~
    ~
    有想法吗?

    好,讲正解:

    很显然,首先我们要做一个最大生成树,用于方便我们求最小值的最大值~这个没有问题吧。
    然后就可以两种选择,因为没有权值修改,所以直接用倍增做也是可以的。
    当然我们现在讲树链剖分。
    如果你刚刚听懂了的话,现在应该直到这棵树应该怎么剖~

    CODE:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    struct Moon{int top,To_tree,heavy_son,father,depth,size;long long key;};
    struct Candy{int x,y,z;};
    int father[10001];
    Moon tree[10001];
    Candy a[50001];
    int n,m,q,tot;
    int To_num[1000001];
    int next[50001],last[50001],len[50001],tov[50001];
    long long f[1000001];
    bool bz[10001];
    bool cmp(Candy a,Candy b){return a.z>b.z;}
    void insert(int x,int y,int z)
    {
    	tov[++tot]=y;
    	len[tot]=z;
    	next[tot]=last[x];
    	last[x]=tot;
    }
    int getfather(int x)
    {
    	if(father[x]==x) return x;
    	father[x]=getfather(father[x]);
    	return father[x];
    }
    void dfs(int t)
    {
    	int y,mx=0;
    	tree[t].size=1;
    	bz[t]=true;
    	for (int i=last[t];i;i=next[i])
    	{
    		y=tov[i];
    		if(!tree[y].depth)
    		{
    			tree[y].depth=tree[t].depth+1;
    			tree[y].father=t;
    			tree[y].key=len[i];
    			dfs(y);
    			tree[t].size+=tree[y].size;
    			if(tree[y].size>mx)
    			{
    				mx=tree[y].size;
    				tree[t].heavy_son=y;
    			}
    		}
    	}
    }
    void dfs2(int t,int k)
    {
    	int y;
    	bz[t]=true;
    	tree[t].top=k;
    	tree[t].To_tree=++tot;
    	To_num[tot]=t;
    	if(tree[t].heavy_son)dfs2(tree[t].heavy_son,k);
    	for (int i=last[t];i;i=next[i])
    	{
    		y=tov[i];
    		if(y!=tree[t].father&&y!=tree[t].heavy_son) dfs2(y,y);
    	}
    }
    void buildtree(int l,int r,int v)
    {
    	if(l==r)
    	{
    		f[v]=tree[To_num[l]].key;
    		return;
    	}
    	int mid=(l+r)/2;
    	buildtree(l,mid,v*2);
    	buildtree(mid+1,r,v*2+1);
    	f[v]=min(f[v*2],f[v*2+1]);
    }
    long long find(int l,int r,int v,int x,int y)
    {
    	if(l==x&&r==y) return f[v];
    	int mid=(l+r)/2;
    	if(y<=mid) return find(l,mid,v*2,x,y);
    	else if(x>mid) return find(mid+1,r,v*2+1,x,y);
    	else return min(find(l,mid,v*2,x,mid),find(mid+1,r,v*2+1,mid+1,y));
    }
    long long query_ans(int x,int y)
    {
    	long long ans=9187201950435737471;
    	while(tree[x].top!=tree[y].top)
    	{
    		if(tree[tree[x].top].depth>tree[tree[y].top].depth)	
    		{
    			ans=min(ans,find(1,n,1,tree[tree[x].top].To_tree,tree[x].To_tree));
    			x=tree[tree[x].top].father;
    		}
    		else
    		{
    			ans=min(ans,find(1,n,1,tree[tree[y].top].To_tree,tree[y].To_tree));
    			y=tree[tree[y].top].father;	
    		}
    	}
    	if(x==y) return ans;
    	if(tree[x].depth<tree[y].depth) ans=min(ans,find(1,n,1,tree[tree[x].heavy_son].To_tree,tree[y].To_tree));
    	else ans=min(ans,find(1,n,1,tree[tree[y].heavy_son].To_tree,tree[x].To_tree));
    	return ans;
    }
    int main()
    {
    	freopen("truck.in","r",stdin);
    	freopen("truck.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	int i,j,xx,yy;
    	for (i=1;i<=n;++i)father[i]=i;
    	for (i=1;i<=m;++i)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
     	sort(a+1,a+1+m,cmp);
     	for (i=1;i<=m;++i)
     	{
     		xx=getfather(a[i].x);	
     		yy=getfather(a[i].y);
     		if(xx!=yy) 
    		{
    			father[xx]=yy;
    			insert(a[i].x,a[i].y,a[i].z);
    			insert(a[i].y,a[i].x,a[i].z);
    		}
    	}
    	tree[1].depth=1;
    	tot=0;
    	for (int i=1;i<=n;i++)
        	if (!bz[i])
        	{
            	tree[i].depth=1;
            	tree[i].father=0;
            	dfs(i),dfs2(i,i);
        	}
    	memset(f,127,sizeof(f));
    	buildtree(1,n,1);
    	scanf("%d",&q);
    	for (i=1;i<=q;++i)
    	{
    		scanf("%d%d",&xx,&yy);
    		if(getfather(xx)!=getfather(yy)) printf("-1
    ");
    		else printf("%lld
    ",query_ans(xx,yy));
    	}
    } 
    

    再推荐几道例题:

    JZOJsenior2753.【2012东莞市选】树(tree)
    【ZJOI2008】树的统计

    树的统计CODE:

    #include<cstdio>
    #include<algorithm>
    #define MAXN 30005
    #define INF 1<<30
    using namespace std;
    int l[MAXN],r[MAXN];
    struct node{int sum,max;};
    node tree[MAXN*4];     
    struct Edge{int v,next;};//v:value
    Edge edge[MAXN*2];
    int head[MAXN],now,val[MAXN],link[MAXN],dep[MAXN],fa[MAXN],sonTree[MAXN],heavySon[MAXN],tot,top[MAXN],num[MAXN];     
    int n,u,v,q;
    void addEdge(int u,int v)
    {
    	now++;
        edge[now].v=v;
        edge[now].next=head[u];
        head[u]=now;
    }
    void buildTree(int now,int l,int r)
    {
        if (l==r) 
        {
        	tree[now].max=val[link[l]];//link(position)
            tree[now].sum=val[link[l]];
            return;
        }
        int mid=(l+r)/2;
        buildTree(now*2,l,mid);
        buildTree(now*2+1,mid+1,r);
        tree[now].max=max(tree[now*2].max,tree[now*2+1].max);
        tree[now].sum=tree[now*2].sum+tree[now*2+1].sum;
    }
     
    void DFS1(int now,int nowFa,int nowDep)
    //HeavySon,Dep,SonTree,Father
    {
        dep[now]=nowDep;fa[now]=nowFa;sonTree[now]=1;
        for (int x=head[now];x!=0;x=edge[x].next)
        {
            if(edge[x].v==nowFa) continue;//bz
            DFS1(edge[x].v,now,nowDep+1);
            sonTree[now]+=sonTree[edge[x].v];
            if(heavySon[now]==0||sonTree[edge[x].v]>sonTree[heavySon[now]]) heavySon[now]=edge[x].v;
        }
    }
    void DFS2(int now,int nowTop)
    //num:The numbering after this point is split 
    //link:The position of the current node in the segment tree
    {
        tot++;
        top[now]=nowTop; num[now]=tot; link[tot]=now;
        if (heavySon[now]==0) return;
        DFS2(heavySon[now],nowTop);
        for (int x=head[now];x!=0;x=edge[x].next)
            if(edge[x].v!=heavySon[now]&&edge[x].v!=fa[now]) DFS2(edge[x].v,edge[x].v);
    }
     
    void init()
    {
        scanf("%d",&n);
        for (int i=1;i<=n-1;i++) 
        {
            scanf("%d %d",&u,&v);
            addEdge(u,v),addEdge(v,u);
        } 
        for (int i=1;i<=n;i++) scanf("%d",&val[i]);
        DFS1(1,0,1); DFS2(1,1);
        buildTree(1,1,n);
    }
     
    void update1(int now,int l,int r,int loc,int delta)
    {
        if (l==r)
        {
            tree[now].sum+=delta;
            tree[now].max+=delta;
            return;
        }
        int mid=(l+r)/2;
        if (loc<=mid) update1(now*2,l,mid,loc,delta);
        else update1(now*2+1,mid+1,r,loc,delta);
        tree[now].max=max(tree[now*2].max,tree[now*2+1].max);
        tree[now].sum=tree[now*2].sum+tree[now*2+1].sum;
    }
     
    int query1(int now,int l,int r,int ql,int qr)
    {
        int ans=0;
        if (ql<=l && r<=qr) return tree[now].sum;
        int mid=(l+r)/2;
        if (ql<=mid) ans+=query1(now*2,l,mid,ql,qr);
        if (qr>mid) ans+=query1(now*2+1,mid+1,r,ql,qr);
        return ans;
    }
     
    int query2(int now,int l,int r,int ql,int qr)
    {
        int ans=-INF;
    	if (ql<=l && r<=qr) return tree[now].max;
        int mid=(l+r)/2;
        if (ql<=mid) ans=max(ans,query2(now*2,l,mid,ql,qr));
        if (qr>mid) ans=max(ans,query2(now*2+1,mid+1,r,ql,qr));
        return ans;
    }
     
    int getMax(int l,int r)
    {
        int f1=top[l],f2=top[r],ans=-INF,nowAns;
        while (f1!=f2)
        {
        	if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
            ans=max(ans,query2(1,1,n,num[f1],num[l]));
            l=fa[f1]; f1=top[l];
        }
        if (dep[l]>dep[r]) nowAns=query2(1,1,n,num[r],num[l]);
        else nowAns=query2(1,1,n,num[l],num[r]);
        ans=max(ans,nowAns);
        return ans;
    }
     
    int getSum(int l,int r)
    {
    	int f1=top[l],f2=top[r],ans=0,nowAns;
        while (f1!=f2)
        {
        	if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
        	ans+=query1(1,1,n,num[f1],num[l]);
            l=fa[f1]; f1=top[l];
        }
        if (dep[l]>dep[r]) nowAns=query1(1,1,n,num[r],num[l]);
        else nowAns=query1(1,1,n,num[l],num[r]);
        ans+=nowAns;
        return ans;       
    }
     
    int main()
    {
        char s[7];
        init();
        scanf("%d",&q);
        for (int i=1;i<=q;i++) 
        {
        	scanf("%s %d %d",s,&u,&v);
        	if (s[0]=='C') update1(1,1,n,num[u],v-val[u]),val[u]=v;//CHANGE
            else if (s[1]=='M') printf("%d
    ",getMax(u,v));//QMAX
            else printf("%d
    ",getSum(u,v));//QSUM
        }
        return 0;
     }
    

    做完这几道题您将会是树链剖分大佬~

    再来一道(我的)题:

    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

    Solution

    这里写图片描述

    CODE:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,m,p,tot,sz;
    int x0,y0,k,kind,x,y;
    int F[400100];
    int last[100010],tov[400010],next[400010],fa[100010];
    struct Tree{int To_tree,depth,top,key,fath,heavy_son,size;}tree[100010];
    struct Edge{int x,y,z;}edge[200010];
    bool cmp(Edge a,Edge b){return a.z<b.z;}
    void insert(int x,int y)
    {
    	tov[++tot]=y;
    	next[tot]=last[x];
    	last[x]=tot;
    }
    int getfather(int x)
    {
    	if(fa[x]==x) return x;
    	fa[x]=getfather(fa[x]);
    	return fa[x];
    }
    void dfs_PREPARE(int x)
    {
    	int mx=0;
    	for (int i=last[x];i;i=next[i])
    	{
    		if(!tree[tov[i]].depth)
    		{
    			tree[tov[i]].fath=x;
    			tree[tov[i]].depth=tree[x].depth+1;
    			tree[tov[i]].key=0;
    			dfs_PREPARE(tov[i]);
    			tree[x].size+=tree[tov[i]].size;
    			if(tree[tov[i]].size>mx)
    			{
    				mx=tree[tov[i]].size;
    				tree[x].heavy_son=tov[i];
    			}
    		}
    	}
    	tree[x].size++;
    }
    void dfs_TREE(int x,int Top)
    {
    	tree[x].top=Top;
    	tree[x].To_tree=++sz;
    	if(tree[x].heavy_son)dfs_TREE(tree[x].heavy_son,Top);
    	for (int i=last[x];i;i=next[i])
    		if(tov[i]!=tree[x].fath&&tov[i]!=tree[x].heavy_son) dfs_TREE(tov[i],tov[i]);
    }
    void change_Add(int l,int r,int v,int x,int y)
    {
    	if(l==r)
    	{
    		F[v]+=y;
    		return;	
    	}
    	int mid=(l+r)/2;
    	if(x<=mid) change_Add(l,mid,v*2,x,y);
    	else change_Add(mid+1,r,v*2+1,x,y);
    	F[v]=F[v*2]+F[v*2+1];
    }
    int Query(int l,int r,int v,int x,int y)
    {
    	if(l==x&&r==y) return F[v];
    	int mid=(l+r)/2;
    	if(y<=mid) return Query(l,mid,v*2,x,y);
    	else if(x>mid) return Query(mid+1,r,v*2+1,x,y);
    	else return Query(l,mid,v*2,x,mid)+Query(mid+1,r,v*2+1,mid+1,y);
    }
    int find(int x,int y)
    {
    	int sum=0,k=0;
    	while (x!=y)
    	{
    		if (tree[x].depth<tree[y].depth) swap(x,y);
    		int u=tree[x].top, v=tree[y].top;
    		if (u==v) 
    		{ 
    			sum+=Query(1,sz,1,tree[y].To_tree,tree[x].To_tree); 
    			return sum; 
    		}
    		if (tree[u].depth<tree[v].depth) swap(x,y),swap(u,v);
    		sum+=Query(1,sz,1,tree[u].To_tree,tree[x].To_tree);
    		x=tree[u].fath;
    	}
    	sum+=Query(1,sz,1,tree[x].To_tree,tree[x].To_tree);
        return sum;	
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	int i,j,xx,yy;
    	for (i=1;i<=m;++i)
    		scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
    	sort(edge+1,edge+1+m,cmp);
    	for (i=1;i<=n;++i) fa[i]=i;
    	for (i=1;i<=m;++i)
    	{
    		xx=getfather(edge[i].x);
    		yy=getfather(edge[i].y);
    		if(xx!=yy)
    		{
    			fa[xx]=yy;
    			insert(edge[i].x,edge[i].y);
    			insert(edge[i].y,edge[i].x);
    			++k;
    		}
    		if(k==n-1) break;
    	}
    	tree[1].depth=1;
    	dfs_PREPARE(1);
    	dfs_TREE(1,1);
    	scanf("%d
    ",&p);
    	while (p--)
    	{
    		scanf("%d%d%d",&x0,&y0,&k);
    		for (i=1;i<=k;++i)
    		{
    			scanf("%d%d%d",&kind,&x,&y);
    			if(kind==2)y=-y;
    			change_Add(1,sz,1,tree[x].To_tree,y);
    			tree[x].key+=y;
    		}
    		printf("%d
    ",find(x0,y0));
    	}
    }
    
  • 相关阅读:
    内核随记(三)同步(1)
    排列算法
    SQLite入门与分析(八)存储模型(3)
    内核随记(一)——理解中断(2)
    dup与dup2系统调用
    内核随记(四)文件系统(1)
    SQLite入门与分析(八)存储模型(2)
    SQLite入门与分析(九)VACUUM命令分析
    c中static变量局部变量
    (i++)+(i++)与(++i)+(++i)
  • 原文地址:https://www.cnblogs.com/Chandery/p/11332811.html
Copyright © 2020-2023  润新知