树链剖分

复杂度nlog2n

边权可以转化成点权 搞一个根 把边权给到边两端深度大的哪个点 这样处理要删一个点 要删的是lca(x,y)

第一步

处理出fa deep size son

void dfs1(int u,int fa,int depth)    //当前节点、父节点、层次深度
{
    f[u]=fa;
    d[u]=depth;
    size[u]=1;    //这个点本身size=1
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa)
            continue;
        dfs1(v,u,depth+1);    //层次深度+1
        size[u]+=size[v];    //子节点的size已被处理,用它来更新父节点的size
        if(size[v]>size[son[u]])
            son[u]=v;    //选取size最大的作为重儿子
    }
}
//进入
dfs1(root,0,1);

第二步

连接重链 处理出top dfn rk 注意先处理重链再处理轻链 保证了重链的dfn值连续

void dfs2(int u,int t)    //当前节点、重链顶端
{
    top[u]=t;
    id[u]=++cnt;    //标记dfs序
    rk[cnt]=u;    //序号cnt对应节点u
    if(!son[u])
        return;
    dfs2(son[u],t);
/*我们选择优先进入重儿子来保证一条重链上各个节点dfs序连续,
一个点和它的重儿子处于同一条重链,所以重儿子所在重链的顶端还是t*/
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=son[u]&&v!=f[u])
            dfs2(v,v);    //一个点位于轻链底端,那么它的top必然是它本身
    }
}

求lca步骤:

如果两个是在一个重链上的(top相同) 直接选深度小的哪个为lca

否则的话每次选两个节点中top深度深的哪个节点跳

修改步骤:

如果和lca有关的话因为dfn是连续的 就相当于在跳的过程中的每个区间都区间修改区间查询 复杂度是log2n

如果是和子树有关的话 就只是和dfs序有关了 也是区间修改区间查询

 

 

将所有未被任何一条树链经过的树边删除,那么剩下的点度数都不能超过22,否则必然非法。

 

那么剩下的图每个连通块都只可能是链,是经典序列问题,把所有区间按左端点排序后单调栈判断即可。

 

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=100010;
int n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed;
int f[N],son[N],size[N],top[N],d[N];
int e[N][2];
int tag[N];
int deg[N];
int G[N],V[N<<1],NXT[N<<1],ED;
bool vis[N];
int ge[N],vv[N],nxte[N],ee;
void NIE(){
    puts("No");
    exit(0);
}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline void adde(int x,int y){vv[++ee]=y;nxte[ee]=ge[x];ge[x]=ee;}
void dfs(int x){
    d[x]=d[f[x]]+1;
    size[x]=1;
    for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
        f[v[i]]=x;
        dfs(v[i]);
        size[x]+=size[v[i]];
        if(size[v[i]]>size[son[x]])son[x]=v[i];
    }
}
void dfs2(int x,int y){
    top[x]=y;
    if(son[x])dfs2(son[x],y);
    for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]);
}
inline void addedge(int x,int y){
    //printf("! %d %d\n",x,y);
    deg[x]++;deg[y]++;
    V[++ED]=y;NXT[ED]=G[x];G[x]=ED;
    V[++ED]=x;NXT[ED]=G[y];G[y]=ED;
}
void dfs3(int x){
    for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
        dfs3(v[i]);
        tag[x]+=tag[v[i]];
    }
    if(x>1&&tag[x])addedge(x,f[x]);
}
inline int lca(int x,int y){
    while(top[x]!=top[y]){
        if(d[top[x]]<d[top[y]])swap(x,y);
        x=f[top[x]];
    }
    return d[x]<d[y]?x:y;
}
int q[N],pos[N];
int all;
struct Seg{
    int l,r;
}seg[N],st[N];
inline bool cmp(const Seg&a,const Seg&b){
    if(a.l!=b.l)return a.l<b.l;
    return a.r>b.r;
}
inline void append(int l,int r){
    if(l>r)swap(l,r);
    seg[++all].l=l;
    seg[all].r=r;
}
inline void check(){
    if(all<=1)return;
    sort(seg+1,seg+all+1,cmp);
    int i;
    int t=0;
    for(i=1;i<=all;i++){
        while(t){
            if(seg[i].r<=st[t].r)break;
            if(seg[i].l<=st[t].r)NIE();
            t--;
        }
        st[++t]=seg[i];
    }
}
inline void solve(int S){
    int cnt=0;
    int i,j;
    while(1){
        vis[S]=1;
        q[++cnt]=S;
        int t=0;
        for(i=G[S];i;i=NXT[i])if(!vis[V[i]]){
            t=V[i];
        }
        if(!t)break;
        S=t;
    }
    //for(i=1;i<=cnt;i++)printf("%d ",q[i]);puts("");
    for(i=1;i<=cnt;i++)pos[q[i]]=i;
    all=0;
    for(i=1;i<=cnt;i++){
        for(j=ge[q[i]];j;j=nxte[j]){
            append(pos[q[i]],pos[vv[j]]);
        }
    }
    check();
}
int main(){
    scanf("%d%d",&n,&m);
    for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1);
    dfs2(1,1);
    for(i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        e[i][0]=x,e[i][1]=y;
        int z=lca(x,y);
        tag[x]++;
        tag[y]++;
        tag[z]-=2;
        adde(x,y);
    }
    dfs3(1);
    for(i=1;i<=n;i++)if(deg[i]>2)NIE();
    for(i=1;i<=n;i++)if(deg[i]==1&&!vis[i])solve(i);
    puts("Yes");
}
View Code

 

posted @ 2018-07-29 20:27  Aragaki  阅读(166)  评论(0编辑  收藏  举报