精华内容
下载资源
问答
  • Kruskal算法是先把所有边按照边大小进行排序,得到一个升序序列,然后遍历每一条边,如果这条边两个点不是在同一个集合,则选择当前(因为排序过,所以当前边也是两点之间最小边),顺便合并这两个...

    AcWing 859. Kruskal算法求最小生成树

    前置问题

    Q:什么是生成树呢?
    A: 在一个图中,由n个点(其实就是所有顶点)和n-1条边所构成的连通子图(结构是一颗树)

    Q:怎么判断哪些点是在同一个连通块?
    A:使用并查集就行了,把每个独立的连通块看作是一个集合

    思想

    Kruskal算法是先把所有边按照边权大小进行排序,得到一个升序的序列,然后遍历每一条边,如果这条边的两个点不是在同一个集合,则选择当前的边权(因为排序过,所以当前边权也是两点之间的最小边权),顺便合并这两个点所在的集合;如果这两个点已经在同一个集合内,则说明之前已经处理过这两点之间的边权了,不需要再次处理

    解决方案

    1.输入点数和边数,初始化存储边数的结构体数组,初始化并查集

    2.以边权大小为标准将数组进行升序排序

    3.对m条边进行处理

    3.1 判断这条边的两个点是否属于同一个集合,如果是,则已经处理了

    3.2 如果不是,则把两个点所在的集合合并,并记录最小边权,同时也记录合并集合的次数

    代码

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N = 1e5 + 10, M = 2e5 + 10, INF = 0x3f3f3f3f;
    //p为并查集数组,n为点数,m为边数,cnt为记录生成树边数的变量,ans为最小生成树的所有边权之和
    int p[N], n, m, cnt, ans;
    
    //图一般是使用邻接矩阵或者邻接表或者链式前向星来存储,但因为要对边权排序,所以使用结构体就方便多了
    struct Edge {
        //a为边的起点,b为边的终点,c为边权
        int a, b, c;
        
        //运算符重载
        bool operator< (const Edge &e) const {
            return c < e.c;
        }
    } edge[M];//声明结构体的同时就创建结构体数组,用来存储图的所有边
    
    //并查集函数,使用了路径压缩
    int find(int x) {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }
    
    //Kruskal算法的实现
    void kruskal() {
    	//先对所有边按边权大小进行排序
        sort(edge, edge + m);
        
        //初始化并查集数组,编号为1~n
        for (int i = 1; i <= n; i++) p[i] = i;
        
        //对排序好的边进行处理
        for (int i = 0; i < m; i++) {
            int a = edge[i].a, b = edge[i].b, c = edge[i].c;
            int x = find(a), y = find(b);    //分别找a和b所在的集合的代表节点
            
            /*如果这两点不是连通的,现在这条边就把这两点连通,并记录这条边的权值
              如果这两点是连通的,就说明之前已经处理过 这两点之间的最小权值了,
              因为边的权值是从小到大排序,所以任意两点之间的最小权值的边已经被处理了,
              剩下的这两点之间的其他边就不需要处理了*/
            if (x != y) {     
                p[x] = y;
                ans += c;   //记录最小的权值
                cnt++;
            }
        }
    
    	/*n个集合经过处理如果只剩下一个集合,那么肯定是进行了n-1次操作
    	  如果操作次数少于n-1,那么图就不连通 */
        if (cnt < n - 1) puts("impossible");
        else cout << ans;
    }
    
    int main() {
        cin >> n >> m;    //输入点数和边数
        for (int i = 0; i < m; i++) {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            edge[i] = {a, b, c};
        }
        kruskal();
        return 0;
    }
    
    展开全文
  • 完全图最小生成

    2019-09-28 16:15:47
    给你一张完全图,每一个点有一个点权为 \(a[i]\),边 \((u,v)\) 的边权为 \(a[u]\) \(xor\) \(a[v]\),最小生成树的和. solution 正解:trie树+贪心 考虑优化kruskal的过程,我们找出边最小的且边的...

    Description

    给你一张完全图,每一个点有一个点权为 \(a[i]\),边 \((u,v)\) 的边权为 \(a[u]\) \(xor\) \(a[v]\),求最小生成树的边权和.

    solution

    正解:trie树+贪心
    考虑优化kruskal的过程,我们找出边权最小的且边的两边没有连通的边,选择连接,方法是在trie树上贪心,首先我们对所有的点建立trie树,然后考虑怎么样连边最优,容易发现,一定是选择二进制下交最多的两个点,那么一定对应trie树上的一个前缀,所以我们只需要dfs trie树,自底向上合并即可,考虑枚举左右子树中size小的,在另一棵子树上遍历求得异或最小值,启发式合并的复杂度是 \(O(n*logn)\) 的,加上trie树的操作,总复杂度为 \(O(n*log2n)\)

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=4000005;
    inline int gi(){
        RG int str=0;RG char ch=getchar();
        while(ch>'9' || ch<'0')ch=getchar();
        while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();
        return str;
    }
    int n,rt=0,bin[30],sz[N],ls[N],rs[N],totnode=0;
    ll ans=0;
    
    il void ins(int &x,int v,int d){
        if(!x)x=++totnode;
        if(d==-1){sz[x]=1;return ;}
        if(bin[d]&v)ins(rs[x],v,d-1);
        else ins(ls[x],v,d-1);
        sz[x]=sz[ls[x]]+sz[rs[x]];
    }
    
    il int qry(int x,int v,int d){
        if(d==-1)return 0;
        if(v&bin[d]){
            if(rs[x])return qry(rs[x],v,d-1);
            return qry(ls[x],v,d-1)+bin[d];
        }
        else{
            if(ls[x])return qry(ls[x],v,d-1);
            return qry(rs[x],v,d-1)+bin[d];
        }
    }
    
    il int merge(int x,int y,int d,int val,int sd){
        if(d==-1)return qry(y,val,sd);
        int ret=1<<30;
        if(ls[x])ret=min(ret,merge(ls[x],y,d-1,val,sd));
        if(rs[x])ret=min(ret,merge(rs[x],y,d-1,val+bin[d],sd));
        return ret;
    }
    
    il void dfs(int x,int d){
        if(d==-1)return ;
        if(ls[x])dfs(ls[x],d-1);
        if(rs[x])dfs(rs[x],d-1);
        if(!ls[x] || !rs[x])return ;
        int l=ls[x],r=rs[x];
        if(sz[l]>sz[r])swap(l,r);
        ans+=merge(l,r,d-1,0,d-1)+bin[d];
    }
    
    void work()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)ins(rt,gi(),29);
        dfs(rt,29);
        cout<<ans<<endl;
    }
    
    int main()
    {
        bin[0]=1;for(int i=1;i<=29;i++)bin[i]=bin[i-1]<<1;
        work();
        return 0;
    }
    

    转载于:https://www.cnblogs.com/Hxymmm/p/7795216.html

    展开全文
  • 思路:判断一条边是不是生成树的可能边,比如u->v那么只要知道u到v的简单路径中,有没有和u->v这条边的边相等的,如果有,那么他们全都是可能边,这个做法可以尝试以下lca,不过笔者并不晓得lca路径上该怎么...

    题目链接
    在这里插入图片描述
    在这里插入图片描述
    思路:判断一条边是不是生成树的可能边,比如u->v那么只要知道u到v的简单路径中,有没有和u->v这条边的边权相等的,如果有,那么他们全都是可能边,这个做法可以尝试以下lca,不过笔者并不晓得lca路径上该怎么改。。。只能用另一种办法,把所有边权相等的边拿来一起考虑,如果一条边的两点已经在一个集合了,他们他们肯定是none,,接着就将这些边建图走tarjan来判断一下有没有桥,如果是桥的话就是any,否则都是least noe。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll; 
    const int maxn=1e5+5;
    int n,m,cnt=0,dfn[maxn],low[maxn],father[maxn];
    vector<pair<int,int>>g[maxn];
    struct node{
    	int u,v,w,id,ans;
    }s[maxn];
    bool cmp(const node &a,const node &b)
    {
    	return a.w<b.w;
    }
    bool cmp1(const node &a,const node &b)
    {
    	return a.id<b.id;
    }
    int findfather(int x)
    {
    	if(x==father[x]) return x;
    	int i=findfather(father[x]);
    	father[x]=i;
    	return i;
    }
    void tarjan(int x,int fa)//tarjan求桥
    {
    	low[x]=dfn[x]=++cnt;
    	for(auto to:g[x])
    	{
    		if(fa==to.second) continue;//记住,这里防止走环的判断是根据边的编号而不是点 
    		if(!dfn[to.first]) 
    		{
    			tarjan(to.first,to.second);
    			low[x]=min(low[x],low[to.first]);
    			if(low[to.first]>dfn[x]) s[to.second].ans=2;
    		}
    		else low[x]=min(low[x],low[to.first]);
    	}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=0;i<=n;++i) father[i]=i;
    	for(int i=1;i<=m;++i)
    	scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].w),s[i].id=i;
    	sort(s+1,s+1+m,cmp);
    	int i=1,j;
    	while(i<=m)
    	{
    		j=i;
    		while(j<=m&&s[i].w==s[j].w) j++;
    		for(int k=i;k<j;++k)//建边 
    		{
    			int fa=findfather(s[k].u),fb=findfather(s[k].v);
    			if(fa==fb) continue;//已经在同一集合的两点肯定不符合要求 
    			g[fa].push_back({fb,k});
    			g[fb].push_back({fa,k});
    			s[k].ans=1;
    		}
    		for(int k=i;k<j;++k)//求桥 
    		{
    			int fa=findfather(s[k].u),fb=findfather(s[k].v);
    			if(fa==fb||dfn[fa]) continue;
    			tarjan(fa,-1);
    		}
    		for(int k=i;k<j;++k)//恢复原状 
    		{
    			int fa=findfather(s[k].u),fb=findfather(s[k].v);
    			if(fa==fb) continue;
    			g[fa].clear();
    			g[fb].clear();
    			low[fa]=low[fb]=dfn[fa]=dfn[fb]=0;
    			father[fa]=fb;
    		}
    		i=j;
    	}
    	sort(s+1,s+1+m,cmp1);
    	for(int i=1;i<=m;++i)
    	if(s[i].ans==0) printf("none\n");
    	else if(s[i].ans==1) printf("at least one\n");
    	else printf("any\n");
    }
    
    展开全文
  • 某自学的树链剖分

    2016-06-18 23:15:42
    我们知道,如果给你一个数组,我们可以利用线段在log2(n)时间内完成区间或单点查询与修改,但是,如果给你一棵,要求点到点间对边或点权的查询与修改,要怎么做呢? 很容易想到最近公共祖先,然后...

    此文用来帮助那些像我一样在这方面迷茫的人

    我们知道,如果给你一个数组,我们可以利用线段树在log2(n)的时间内完成区间或单点的查询与修改,但是,如果给你一棵树,要求点到点间对边权或点权的查询与修改,要怎么做呢?

    很容易想到树上求最近公共祖先,然后暴力修改查询,但是那样做的复杂度会很高(log2(n)+n)左右,明显无法满足要求,那么能不能把树上的点或边放到线段树等数据结构中,利用数据结构的优点完成这一操作呢?

    于是我们引入了树链剖分,,,(剖读(pou) = =

    我对树链剖分的理解就是把树进行分割,把最多的点放在一起,做到把树变成链。

    首先介绍几个概念:

    重儿子:对于点u,她的子节点v中最大的是重儿子,最大的意思是该子树所包含的节点数最多

    轻儿子:不是重儿子的儿子

    重边:节点u与重儿子v的连边

    轻边:节点u与轻儿子的连边

    重链:重边连成的链

    我们要实现她,可能会用到以下几个数组:

    siz[i]:节点i为根的子树所包含的节点个数,fa[i]:i的爸爸,son[i]:i的重儿子编号,dep[i]:i的深度(根节点深度为1);

    top[i]:节点i所处重链的顶端节点(如果她与父节点连边为轻边,则top[i]=i),p[i]:节点i在数据结构中所处的位置。

    算法流程:先进行一遍dfs,可以求出siz,fa,son,dep这个都会把。

    再进行一遍dfs,求出top,同时求出p具体做法:

    如果有重儿子,top[son[x]]=top[x],p[son[x]]=++Index,同时对他dfs

    如果没有,则top[v]=v,pos[v]=++Index,也对他dfs

    然后,再把每个点插入到线段树中,就完成了建树。

    那么怎么进行点到点的操作呢?

    观察到因为重链上的点一定是在线段树中连续的,所以如果要对同一重链上两点进行查询,可以在O(log2(n))内用线段树完成。

    那么如果不在同一重链呢?

    然后就是玄学所在....

    设我们要求u,v间的最大值,那么设uf=top[u],vf=top[v];如果(vf!=uf) 说明两点不在同一重链,那么找出uf和vf中深度大的,设记为u,uf,显然,x和y在线段树中的连续位置,求出两点间信息,令u=fa[uf],uf=top[u,重复上述操作,直到两点处于同一重链。

    现在就好办了,再次找出深度大的点,然后线段树就好了。

    (虽然并不会随便注意到。。。  为什么这样做的时间复杂度就会小呢?我们可以注意到(虽然并不会直接注意到。。。:任意一个点到根节点上的轻边与重链条数之和一定<=log2(n),也就是说一个点到根节点的操作至多只会进行log2(n)次log2(n)的线段树查询,复杂度上限即二者乘积。

    如果只看文字无法理解,大家可以尝试画图,也可以看一下下面的代码,相信应该会很快掌握。

    例题:BZOJ1036,树链剖分模板题。

    题意:一棵树,支持单点修改,点到点求最大值,求和操作。

    运用上述树链剖分知识,直接上代码(为什么我的代码总是那么长啊QAQ

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    typedef long long ll;
    const int maxn = 30010;
    using namespace std;
    int n,m;
    int first[maxn];
    struct edg
    {
    	int next;
    	int to;
    }e[maxn<<1];
    ll val[maxn];
    int top[maxn],fa[maxn],dep[maxn];
    int son[maxn],p[maxn],siz[maxn];
    int e_sum;
    int cnt;
    struct sg_tree
    {
    	ll mmax;
    	ll sum;
    }node[maxn<<2];
    void add_edg(int x,int y)
    {
    	e_sum++;
    	e[e_sum].next=first[x];
    	first[x]=e_sum;
    	e[e_sum].to=y;
    }
    void updata(int rt)
    {
    	node[rt].mmax=max(node[rt<<1].mmax,node[rt<<1|1].mmax);
    	node[rt].sum=node[rt<<1].sum+node[rt<<1|1].sum;
    }
    void dfs1(int x,int f,int d)
    {
    	dep[x]=d;
    	siz[x]=1;
    	for(int i=first[x];i;i=e[i].next)
    	{
    		int w=e[i].to;
    		if(w!=f)
    		{
    			dfs1(w,x,d+1);
    			siz[x]+=siz[w];
    			if(siz[w]>siz[son[x]])
    				son[x]=w;
    			fa[w]=x;
    		}
    	}
    }
    void dfs2(int x,int f,int y)
    {
    	top[x]=y;
    	if(son[x])
    	{
    		p[son[x]]=++cnt;
    		dfs2(son[x],x,top[x]);
    	}
    	for(int i=first[x];i;i=e[i].next)
    	{
    		int w=e[i].to;
    		if(w!=f&&w!=son[x])
    		{
    			p[w]=++cnt;
    			dfs2(w,x,w);
    		}
    	}
    }
    void insert(int l,int r,int rt,int pos,ll x)
    {
    	if(l==r)
    	{
    		node[rt].mmax=x;
    		node[rt].sum=x;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(pos<=mid) insert(lson,pos,x);
    	else insert(rson,pos,x);
    	updata(rt);
    }
    ll query_max(int l,int r,int rt,int left,int right)
    {
    	if(left<=l&&r<=right) return node[rt].mmax;
    	int mid=(l+r)>>1;
    	if(right<=mid) return query_max(lson,left,right);
    	else if(left>mid) return query_max(rson,left,right);
    	else return max(query_max(lson,left,mid),query_max(rson,mid+1,right));
    }
    ll getmax(int x,int y)
    {
    	int f1=top[x],f2=top[y];
    	ll tmp=-0x3f3f3f3f;
    	while(f1!=f2)
    	{
    		if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
    		tmp=max(tmp,query_max(1,n,1,p[f1],p[x]));
    		x=fa[f1],f1=top[x];
    	}
    	if(dep[x]<dep[y]) swap(x,y);
    	return max(tmp,query_max(1,n,1,p[y],p[x]));
    }
    ll query_sum(int l,int r,int rt,int left,int right)
    {
    	if(left<=l&&r<=right) return node[rt].sum;
    	int mid=(l+r)>>1;
    	if(right<=mid) return query_sum(lson,left,right);
    	else if(left>mid) return query_sum(rson,left,right);
    	else return query_sum(lson,left,mid)+query_sum(rson,mid+1,right);
    }
    ll getsum(int x,int y)
    {
    	int f1=top[x],f2=top[y];
    	ll tmp=0;
    	while(f1!=f2)
    	{
    		if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
    		tmp+=query_sum(1,n,1,p[f1],p[x]);
    		x=fa[f1],f1=top[x];
    	}
    	if(dep[x]<dep[y]) swap(x,y);
    	return tmp+query_sum(1,n,1,p[y],p[x]);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<n;i++)
    	{
    		int x,y,z;
    		scanf("%d%d",&x,&y);
    		add_edg(x,y);
    		add_edg(y,x);
    	}
    	for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
    	dfs1(1,-1,1);
    	p[1]=++cnt;
    	dfs2(1,-1,1);
    	for(int i=1;i<=n;i++)
    	   insert(1,n,1,p[i],val[i]);
    	scanf("%d",&m);
    	while(m--)
    	{
    		char c[10];
    		int x,y;
    		scanf("%s%d%d",c,&x,&y);
    		if(c[1]=='H') insert(1,n,1,p[x],y);
    		else if(c[1]=='M') printf("%lld\n",getmax(x,y));
    		else printf("%lld\n",getsum(x,y));
    	}
    	return 0;
    }
    一开始WA了一次,后来发现自己真是萌的出血,点权可能是负数,但是我最大值一开始是0,不WA才怪!

    以后不管怎么样,求Max先赋-0x3f3f3f3f,求Min先赋0x3f3f3f3f  !!!!!

    感悟:自学真的非常重要,不能一直等着人教,花一点时间学一会总会学懂,不要怕看似困难的知识。


    展开全文
  • 在一棵树里,每一条边都有权值(树里面两个点之间的边),那么树上最远的距离就为树的直径,当然我们大多数做的都是边权为1的树,这个时候也就是树上距离最远的两个点 解树的直径的两种方法 1.树形DP ans 来记录...
  • 思路:题意要求严格次小生成那么我们看次小生成树怎么求。 第一步这里打错了一个是求次小生成。这里我们不但要求任意连点间边最大值还要求次大值,因为当最大树边和当前枚举边长度相同时,就不能替换了...
  • Description给出两棵n个点的树,每个点有对应(可正可负)。你需要选择一个点集,使得: 1:在两棵中这个点集都是一个联通快 2:这个点集中所有点和最大。 这个最大值。 n首先让我们来思考一下...
  • 题目给一棵,边带有权值,每一点到其他点路径上最大和。 上任意两点路径都可以看成是经过某棵子树根路径,即路径=两个点到根路径权的和,于是果断分治。 对于每次分治子树,计算其所有结点到...
  • 做法是把一开始树的直径上的边的边改成$ -1 $,那么当我们第二次用这些边做环时就抵消了一开始的贡献。 所以答案就是边的数量*2 - 一开始树的直径 - 后来树的直径 P.S. 第二次求树的直径时只能dp 代码 #...
  • 题目 题意 给一颗带边的树,你可以选择kkk个顶点的一条链,链上的顶点都开商店,问怎么使得每一个点到最近的商店的最大值最小。 思路 这里考虑树的直径的中心,肯定是开商店的。...求树的直径的中心: 1.先...
  • 主席树其实就是线段树的优化。 我们考虑这道模板题。 如果用暴力的方法做,肯定会Tle。 那么我们想一想能不能用线段树来优化一下。 先简单化一下题目,如果的是1~m(m&amp;lt;=n)的第k大值,怎么用线段树...
  • 题目传送门 (这次英语题应该比较好懂吧……) 大意是 给定一棵有nn个点的树,每个点有...现在路径上点权的乘积,继续点分治,怎么合并答案? 我们已经统计出一棵子树到重心权值积了,因为这些乘积mm对MOD\t
  • (3.16

    2019-10-07 04:44:46
    求和:(lca,从链两边往上跳,跳到lca) 有序:往上跳时候,右子(下几层)下面点值最大,正序推 左子树(下几层)点值最小,逆序推 再了解一下大佬教正解 大致思想:其实处理很像是快读,re...
  • Kruskal重构

    2019-01-09 19:05:00
    普通Kruskal最小生成,是在加边过程中,利用冰碴鸡并查集检查是否会生成环,直接合并并查集并对边端点u,v直接连边 而生成kruskal重构,在加边过程中,则会进行如下操作: 建立一个新点x,点为边...
  • 给一棵,给出边(边权为距离)把m个点标记为黑色,让黑色点两两之间距离加白色点两两之间距离最大值。 怎么做呢?我还是菜了呀 dp[x][i] 代表x子树上有i个黑点对答案贡献最大值。 然后就没有然后...
  • 题目传送门  戳我来传送 ... 然后考虑这样的生成树的个数怎么求,根据某个经典套路,我们可以容斥。  因为可以求出边的最大公约数为$i$的倍数的生成树的个数$F(i)$,所以减去它的倍数的$f$就是$f(i)$...
  • 题意:给定一棵n个节点的树,q组询问,每次询问找m个关键点,m个关键点两两之间距离和、距离最大值和最小值 n个点的树,对k个关键进行操作(询问)一眼过去一般都是虚题 因为原树边是1,所以虚上两...
  • 牛客 最长链 图

    2019-12-28 15:23:02
    给一棵树 让最长的树链: 傻逼,这不就树的直径吗 给每个点权值,找出的链要满足链上的点的权值的 gcd > 1 然后 找一个最长的 所以怎么做呢? 一直想不出来没想到就是暴力,怎么暴呢?就是枚举质数,但是枚举...
  • 你需要在每次操作之后出这棵树的最大独立集的权值大小。 题目分析 假如没有修改操作,这题怎么做呢?设axaxa_x为xxx的点,f(x,0/1)f(x,0/1)f(x,0/1)表示xxx这个点不选/选的情况下,其子树中的最大独立集权...
  • 给定一棵nnn个点带边权的树,你可以删去其中KKK条边,再连上KKK条边权为000边,价值是大路径所有方案中你能得到最大价值。 n,m≤3×105n,m\leq 3\times 10^5n,m≤3×105 【解题思路】 考虑已经删掉了边,...
  • 测试地址:Envy 题目大意: 给定一个带权...首先,判断kkk条边是不是能同时在最小生成树中,实际上只要求出包含这些边的边和最小的生成树的权值,再和最小生成树比对即可。 怎么求呢?首先把kkk条边先连上,形成...
  • Graph 题目链接 题目大意 给出一颗,有两种操作,添加一条边,删除一条边...然后考虑到了并查集,但是不会两个集合合并,也就是合并n - 1次,但是怎么求两个集合里数各挑一个异或最小值,然后就不会了好菜,连异
  • UVA 10600 & 次小生成

    2014-07-18 08:42:00
    但有必要提醒的是,交换过来的这样一条边,必须是未成使用过的(即不是最小生成树的边)。而且,交换走的边,必须是已存在的,而且是两点间最短路径的最大边的边。 所以,可以在最小生成树时这最大边的边。...
  • 【清华集训模拟】

    2018-01-19 22:37:36
    把这棵划分成若干条不相交路径,使得每条路径上和均非负方案数。 n先考虑Dp怎么写,设Fx表示以x为根子树已经覆盖完成了. 转移两条链,把这两条链上挂着F值乘起来就是答案。 那么这个东西要如何...
  • P2633 Count on a tree 题目链接 题目大意 给一棵,有点,每次询问,x到y路径上点点权的第k小是多少? 强制在线, 题解 第k小,肯定会想到主席。...然后现在知道了根到每个点,怎么求一个路径上? 当然是 T
  • 链剖分(初学)

    2019-09-18 23:32:07
    2.修改上两点简单路径上(点) 如果只用单独考虑,那么第一个就是上差分,第二个是倍增,可是如果同时要有两个操作,那么时间复杂度就会大幅度退化,所以现在要用一个新方法:链剖分 它将一棵剖成...
  • 先%一发机房各路祖传树剖大师%%%。 近来总有人向我安利树剖LCA,然鹅我...其实主要是学习它的思想,而它实际包含的知识(线段树(大多情况用线段树,理论上应该还能用其他数据结构维护)、dfs序与时间戳、树的...
  • 先对图生成最小树,然后考虑怎么求两点之间最长边,路径必过LCA,那么就用倍增思想来求瓶颈路; 在用倍增求LCA时,有一个数组p[i][j],定义为 i 点 向上 跳 2 j 次方步到达点,那么同样,建立一个新...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 154
精华内容 61
关键字:

树的权怎么求