1. 程式人生 > >Gym - 101630L Laminar Family (樹鏈剖分維護區間出現的段數)

Gym - 101630L Laminar Family (樹鏈剖分維護區間出現的段數)

 

解題思路:先把每個查詢的距離從大到小排序,然後我們再列舉查詢,這樣就不用考慮包含的情況了,因為小的肯定可以被大的包含,因此我們就只用考慮相交的情況了,關於相交,我們用區間染色的思想,然後查詢區間出現的顏色個數即可,我這裡用的是區間查詢顏色段數。

#include<iostream>
#include<deque>
#include<memory.h>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<vector>
#include<math.h>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#define INF (0x3f3f3f3f)
using namespace std;
typedef long long int ll;
const int MAXN=100010;

//鄰接圖部分
struct Edge{
    int to;
    int next;
}e[MAXN*2];
int head[MAXN],edge_num;
void insert_edge(int u,int v){
    e[edge_num].to=v;
    e[edge_num].next=head[u];
    head[u]=edge_num++;
}

//樹鏈剖分部分
int top[MAXN];//重鏈頂點
int fa[MAXN];//父親節點
int deep[MAXN];//深度
int size[MAXN];//子樹大小
int pos[MAXN];//dfs序
int son[MAXN];//重兒子是哪個
int SEG;//dfs序當前點

void init(){
    edge_num=0;
    memset(head,-1,sizeof(head));
    SEG=1;
    memset(son,-1,sizeof(son));
}

//求出fa,deep,num,son
void dfs1(int u,int pre,int d){
    deep[u]=d;
    fa[u]=pre;
    size[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v!=pre){
            dfs1(v,u,d+1);
            size[u]+=size[v];
            if(son[u]==-1||size[v]>size[son[u]])
                son[u]=v;
        }
    }
}

//求出top和pos,求的過程中,先求重鏈上的dfs序
void dfs2(int u,int sp){
    top[u]=sp;
    pos[u]=SEG++;
    if(son[u]!=-1){
        dfs2(son[u],sp);
    }
    else{
        return;
    }
    
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v!=son[u]&&v!=fa[u]){
            dfs2(v,v);
        }
    }
}

//線段樹維護區間出現的段數部分
int lc[MAXN<<2];//線段樹陣列,看你要存什麼
int rc[MAXN<<2];//線段樹陣列,看你要存什麼
int num[MAXN<<2];
int Add[MAXN<<2];//懶惰標記,這裡是區間累加
int n;
//更新節點資訊,這裡是求最值
void pushup(int rt){
    lc[rt]=lc[rt<<1];
    rc[rt]=rc[rt<<1|1];
    num[rt]=num[rt<<1]+num[rt<<1|1]-(rc[rt<<1]==lc[rt<<1|1]);//<<1為*2.<<1|1為*2+1,即左子樹和右子樹
}

//建樹
void build(int l,int r,int rt){//l,r表示當前區間,rt表示當前區間線上段樹陣列中的位置
    Add[rt]=-1;
    lc[rt]=0;
    rc[rt]=0;
    if(l==r){
        num[rt]=1;
        return;
    }
    int m=(l+r)>>1;//>>1等於/2
    //遞迴建樹
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);//建完左右子樹後,更新當前節點的值
}

//下推函式,ln,rn為左子樹,右子樹的數字數量。
void pushdown(int rt,int l,int r){
    if(l==r){
        return;
    }
    if(Add[rt]!=-1){//如果存在下推標記
        //將標記下推至子節點
        Add[rt<<1]=Add[rt];
        Add[rt<<1|1]=Add[rt];
        //修改子節點的值
        lc[rt<<1]=rc[rt<<1]=Add[rt];
        lc[rt<<1|1]=rc[rt<<1|1]=Add[rt];
        num[rt<<1]=num[rt<<1|1]=1;
        //清除標記
        Add[rt]=-1;
    }
}

//區間修改,比點修改多了個R引數,這裡是區間+C,與查詢同理,在查詢時修改
void update(int L,int R,int C, int l, int r, int rt){
    if(L==l&&r==R){//如果當前區間在查詢區間內,直接對其進行修改
        lc[rt]=C;//C*當前區間含有的數字個數
        rc[rt]=C;//C*當前區間含有的數字個數
        num[rt]=1;
        Add[rt]=C;//增加Add標記,表示本區間的值正確,子區間的值仍需要根據Add的值來調整
        return;
    }
    int m=(l+r)>>1;
    pushdown(rt,l,r);//必須下推標記,因為有pushup
    if(R <= m) //與查詢同理
        update(L,R,C,l,m,rt<<1);
    else{
        if(L >  m)
            update(L,R,C,m+1,r,rt<<1|1);
        else{
            update(L,m,C,l,m,rt<<1);
            update(m+1,R,C,m+1,r,rt<<1|1);
        }
    }
    
    pushup(rt);//更新本節點資訊
}

int query(int L, int l, int r, int rt){
    if(L==r)
        return rc[rt];
    if(L==l)
        return lc[rt];
    pushdown(rt,l,r);
    int m=(l+r)>>1;
    if(L<=m)
        return query(L,l,m,rt<<1);
    else
        return query(L,m+1,r,rt<<1|1);
}


//查詢,這裡為求最值,LR代表要查詢的區間,lr代表當前區間,rt表示當前節點在陣列中的實際位置
int query(int L,int R,int l,int r,int rt){
    if(L==l&&r==R)//如果當前區間在查詢區間內,直接返回當前存的值
        return num[rt];
    int m=(l+r)>>1;
    
    //每次查詢都下推標記,保證值正確
    pushdown(rt,l,r);
    
    if(R<=m)
        return query(L,R,l,m,rt<<1);
    else{
        if(L>m)
            return query(L,R,m+1,r,rt<<1|1);
        else
            return query(L,m,l,m,rt<<1)+query(m+1,R,m+1,r,rt<<1|1)-(rc[rt<<1]==lc[rt<<1|1]);
    }
}


//線段樹+樹鏈剖分部分
int query(int u,int v){
    int f1=top[u],f2=top[v];
    int temp=0;
    while(f1!=f2){
        if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        temp+=query(pos[f1],pos[u],1,n,1)-(query(pos[fa[f1]],1,n,1)==query(pos[f1],1,n,1));
        // temp+=query(pos[f1],pos[u],1,n,1)-(A[pos[fa[f1]]]==A[pos[f1]]);
        u=fa[f1];
        f1=top[u];
    }
    if(deep[u]>deep[v])
        swap(u,v);
    temp+=query(pos[u],pos[v],1,n,1);
    return temp;
}

void update(int u,int v,int c){
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        update(pos[f1],pos[u],c,1,n,1);
        
        u=fa[f1];
        f1=top[u];
    }
    if(deep[u]>deep[v])
        swap(u,v);
    update(pos[u],pos[v],c,1,n,1);
}


int LCA(int u, int v)
{
    int uu = top[u], vv = top[v];
    while (uu != vv) {
        if (deep[uu] < deep[vv]) {
            swap(uu, vv);
            swap(u, v);
        }
        u = fa[uu]; uu = top[u];
    }
    if (deep[u] < deep[v])
        swap(u, v);
    return v;
}

struct Query{
    int u,v,d;
}Q[MAXN];
bool cmp(Query &a,Query &b){
    return a.d>b.d;
}

int main(){
    
    int q;
    scanf("%d%d",&n,&q);
    init();
    int u,v;
    for(int i=0;i<n-1;i++){
        scanf("%d%d",&u,&v);
        insert_edge(u,v);
        insert_edge(v,u);
    }
    
    dfs1(1,0,0);
    dfs2(1,1);
    build(1,n,1);
    
    for(int i=0;i<q;i++){
        scanf("%d%d",&u,&v);
        Q[i].u=u;
        Q[i].v=v;
        Q[i].d=deep[u]+deep[v]-2*deep[LCA(u,v)];
    }
    
    sort(Q,Q+q,cmp);
    for(int i=0;i<q;i++){
        if(query(Q[i].u,Q[i].v)>1){
            cout<<"No"<<endl;
            return 0;
        }
        update(Q[i].u,Q[i].v,i+1);
    }
    
    cout<<"Yes"<<endl;
    return 0;
}