POJ3417Network(LCA+树上查分||树剖+线段树)

Yixght is a manager of the company called SzqNetwork(SN). Now she‘s very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN‘s business rival, intents to attack the network of SN. More unfortunately, the original network of SN is so weak that we can just treat it as a tree. Formally, there are N nodes in SN‘s network, N-1 bidirectional channels to connect the nodes, and there always exists a route from any node to another. In order to protect the network from the attack, Yixght builds M new bidirectional channels between some of the nodes.

As the DN‘s best hacker, you can exactly destory two channels, one in the original network and the other among the M new channels. Now your higher-up wants to know how many ways you can divide the network of SN into at least two parts.

Input

The first line of the input file contains two integers: N (1 ≤ N ≤ 100 000), M (1 ≤ M ≤ 100 000) — the number of the nodes and the number of the new channels.

Following N-1 lines represent the channels in the original network of SN, each pair (a,b) denote that there is a channel between node a and node b.

Following M lines represent the new channels in the network, each pair (a,b) denote that a new channel between node a and node b is added to the network of SN.

Output

Output a single integer — the number of ways to divide the network into at least two parts.

Sample Input

4 1
1 2
2 3
1 4
3 4

Sample Output

3

题意:

一棵树,后来加了m条新边,形成了一个有环无向图,问删去原树上一条边和m条新边中的一条,使得图不连通。这样的方案有多少。

思路:

以前看到过,完全没有思路,放弃了。今天又看到了,还是没有思路。。。GG

把原图dfs建立成有根树,假设1是根节点,再想,有方向可能就有思路了,oh,还是没有。。。GG

  • 试着手动在两点之间加一条新边,我们看到形成了一个环。
  • 对于环上加的一条新边,两点间每一条原树边都多进入了一个环。
  • 1,      如果一条边进入了两个环,则不用再考虑它。
  • 2,      如果只进入一个环,则它与对应的新边是一组答案。
  • 3,      如果一条表没有进入任何环,那它与任何一条新边是一组答案。
  • 对每一条新边e,其端点为u,v,LCA(u,v)=a,则把路径u-a-v上每一条边加1,最后处理即可。
  • 显然可以用线段树+lazy做。数据上感觉可以过。但是学到了树上差分。。。像前缀和一样。感觉很简单,但是没想到。。。

:sum[u]+1;sum[v]+1;sum[a]-2; 由叶子向根root累加即可。

注意:对象是边,而不是顶点的sum[]。

我写的是树剖(因为在练习树剖),注意找LCA的时候注意 if(dpt[u]<dpt[v]) 是刷新。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#define swap(a,b) {a^=b;b^=a;a^=b;}
using namespace std;
const int maxn=400010;
int Laxt[maxn],Next[maxn],To[maxn],cnt;
int fa[maxn],son[maxn],size[maxn],dpt[maxn],top[maxn];
int n,m,ans,sum[maxn];
void add(int u,int v)
{
    Next[++cnt]=Laxt[u];
    Laxt[u]=cnt; To[cnt]=v;
}
void dfs1(int u,int pre)
{
    fa[u]=pre;dpt[u]=dpt[pre]+1;size[u]=1;son[u]=0;
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i]; if(v==pre) continue;
        dfs1(v,u);size[u]+=size[v];
        if(!son[u]||size[v]>size[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int Top)
{
    top[u]=Top; if(!son[u]) return ; dfs2(son[u],Top);
    for(int i=Laxt[u];i;i=Next[i])
        if(To[i]!=fa[u]&&To[i]!=son[u]) dfs2(To[i],To[i]);
}

int dfs3(int u,int pre)
{
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i];
        if(v!=pre){
            int tmp=dfs3(v,u);
            sum[u]+=tmp;
            if(tmp==0) ans+=m;
            if(tmp==1) ans+=1;
        }
    }
    return sum[u];
}
int find(int u,int v)
{
    int a=top[u],b=top[v];
    while(a!=b){
        if(dpt[a]<dpt[b]) { swap(a,b);swap(u,v);}
        u=fa[a]; a=top[u];
    }
    if(dpt[u]<dpt[v]) return u; return v;
}
int main()
{

    while(~scanf("%d%d",&n,&m)){
        memset(Laxt,0,sizeof(Laxt)); cnt=0;
        memset(sum,0,sizeof(sum)); ans=0;
        for(int i=1;i<n;i++){
            int v,u; scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }
        dfs1(1,0);
        dfs2(1,1);
        for(int i=1;i<=m;i++){
            int u,v; scanf("%d%d",&u,&v);
            int anc=find(u,v);
            sum[u]++;sum[v]++;sum[anc]-=2;
        }
        dfs3(1,0);
        printf("%d\n",ans);
    } return 0;
}
时间: 2024-11-09 05:13:25

POJ3417Network(LCA+树上查分||树剖+线段树)的相关文章

[GX/GZOI2019]旧词(树上差分+树剖+线段树)

考虑k=1的做法:这是一道原题,我还写过题解,其实挺水的,但当时我菜还是看题解的:https://www.cnblogs.com/hfctf0210/p/10187947.html.其实就是树上差分后值为1. 考虑k>1的做法:其实可以再次树上差分,给每个点i赋值v[i]=dep[i]k-dep[i-1]k,然后还是和原来一样开一棵线段树,记录一个val[rt]表示当前节点内区间v值的和,以及sum[rt]表示区间值.修改时打标记,只需要将sum[rt]+=v*val[rt],lazy[rt]+

BZOJ4034 树上操作(树剖 线段树大模板)

BZOJ4034 long long 是大坑点 貌似long long 跟int 乘起来会搞事情?... A了这题线段树和树剖的基础OK 嘛 重点过掉的还是线段树区间更新的lazy tag吧 #include<cstdio> #include<cstring> #define N 100001 using namespace std; struct ed{ int nxt,to; }e[N*2]; int ne=0,head[N]; long long int w0[N]; str

poj3728The merchant树剖+线段树

如果直接在一条直线上,那么就建线段树 考虑每一个区间维护最小值和最大值和答案,就符合了合并的条件,一个log轻松做 那么在树上只要套一个树剖就搞定了,多一个log也不是问题 注意考虑在树上的话每一条链都有可能是正着被用和反着被用,所以存两个答案 所以维护信息只需要一个merge和一个reverse 代码如下: 1 #include <cstdio> 2 #include <iostream> 3 #define mid (l+r>>1) 4 using namespac

bzoj 5210: 最大连通子块和【动态dp+树剖+线段树+堆】

参考:https://www.cnblogs.com/CQzhangyu/p/8632904.html 要开longlong的 首先看dp,设f[u]为必选u点的子树内最大联通块,p[u]为不一定选u的子树内最大联通块,转移很显然就是f[u]=max(Σf[v],0),p[u]=max(max(p[v]),f[u]) 然后看动态的部分,设g是不算重儿子的f,然后每条链上的真实f值要用一棵线段树维护g来得到,具体形式是f[u]=max(g[v]+f[hs[v]],0),是一个最长连续子序列的形式,

BZOJ3631: [JLOI2014]松鼠的新家 树链剖分/lca/树上查分

求n次lca后树上差分. 每次移动时在起始点和终点各打一个start标记,在LCA和LCA的父节点处各上打一个end标记,然后深搜,start标记一直上传,遇到end标记就停止,最后再处理一下就行 % PoPoQQQ大爷 #include<bits/stdc++.h> #define rep(i,l,r) for(int i=l;i<=r;i++) #define N 310000 using namespace std; int head[N],dep[N],sz[N],son[N],

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常young 的她写了如下的算法: 1: function Add(x

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.

区间树和线段树

注意:区间树和线段树不一样哦,线段树是一种特殊的区间树. 区间树: 区间树是在红黑树基础上进行扩展得到的支持以区间为元素的动态集合的操作,其中每个节点的关键值是区间的左端点.通过建立这种特定的结构,可是使区间的元素的查找和插入都可以在O(lgn)的时间内完成.相比于基础的红黑树数据结构,增加了一个max[x],即以x为根的子树中所有区间的断点的最大值.逻辑结构如下所示: 区间树具有和红黑树一样的性质,并且区间树的基础操作和红黑树的基础操作一样.(具体红黑树的操作,参见http://blog.cs