Gym - 101630L Laminar Family (樹鏈剖分維護區間出現的段數)
阿新 • • 發佈:2018-11-17
解題思路:先把每個查詢的距離從大到小排序,然後我們再列舉查詢,這樣就不用考慮包含的情況了,因為小的肯定可以被大的包含,因此我們就只用考慮相交的情況了,關於相交,我們用區間染色的思想,然後查詢區間出現的顏色個數即可,我這裡用的是區間查詢顏色段數。
#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; }