Dash Speed [good question, partition, merge disjoint-set by rank]

Dash Speed

Judge Online : NOIP2016 ten joint measurement, Claris # 2 T3

The Label : Good question, partition, merge disjoint-set by rank, LCA

Title Description

Drag racing holy mountain is a bit bit of the town. In a hill there are n bit square, sequentially numbered from 1 to n, n between the squares by - a two-way driveway directly or indirectly connected together to form a tree structure.

Since each lane of construction supplies and time are different, it is possible to use two digits li, ri represents the quantization interval bear one lane only when the car is not less than and not more than ri li speed past this lane when it does not cause damage to the road surface.

Byteasar recently bought a sports car, he wanted a bit mountains soared a car. Byteasar plans to select two different points S, T, and then running them on the shortest path tree and not to harm any of the above one lane.

Byteasar do not like to change the speed, so he will tell you his speed. In order to select the most suitable speed, Byteasar total will ask you to m times. Please help him find a legitimate way, so that the number of through lanes on the path as much as possible.

Entry

The first line contains two positive integers n, m, and the total number of the total number of squares interrogation FIG.

Next, n - 1 rows, each row of four positive integer ui, vi, li, ri, vi and ui represents a connecting two lane and receiving interval [li, ri].

Next m lines each a positive integer Qi, respectively, each interrogation of the vehicle speed.

Export

M output lines, each an integer, wherein the output speed of the i-th row when the length of the longest path Qi, if no valid path 0 is output.

Sample

Input#1

5 3     
3 2 2 4
1 5 2 5
4 5 2 2
1 2 3 5
1
2
3

Output#1

0
2
3

Sample Explanation:
When the vehicle 1 is legitimate path does not exist.
When the vehicle speed is 2, may be selected 1-5-4 this path length is 2.
3 when the vehicle speed is to be selected 3-2-1-5 this path length is 3

answer

Substack1: violence

For each inquiry direct enumeration an endpoint, then dfs again, looking for maximum distance.

Time complexity is \ (O (M \ CDOT N ^ 2) \) , the score 20 is desirable.

Substack2: tree degradation and l = 1 to chain

Since l is not restricted, as long as the current edge r of greater than or equal to limit Lim, it will be passed. Consider offline query, keywords in r and sort all edges connected inquiry, maintaining communication with disjoint-set block, each block of the communication record two values \ (mA, mi The \) , which represents the communication block node max / min the depth of the chain length, the current block is configured for communicating \ (mA-mi the \) .

Time complexity is \ (O (NlogN M +) \) , the score 40 in combination Substack1 desired.

Substack3: Tree chain degenerate into all cases

Compared Substack2, here also consider limiting the l.

Enumerate the current vehicle speed , if the speed limit is a side { \ (L, R & lt \) }, then the vehicle speed is \ (L \) was added this edge even when the vehicle speed is \ (r + 1 \) deleted even this edge, for each vehicle speed, the answer to the maximum current for all even chain length of the edges formed .

Maintenance can use tree line . Specific maintenance as follows, for each node of the tree segments, provided the corresponding side of the tree in the range of \ ((L, R & lt) \) , maintains three values \ ((Li, RI, S) \) , \ (Li \) represents \ (L \) maximum continuous power to the right length extending, \ (RI \) represents \ (R & lt \) to the left of the maximum continuous length can be extended, \ (S \) represents the maximum chain length within the interval .

Time complexity is \ (O (NlogN M +) \) , the score 60 in combination Substack1 desired.

Slicing the case of the first three code is as follows:

#include<bits/stdc++.h>
using namespace std;
#define R register

bool nc1;
const int N=70010;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
struct edge{
    int to,nxt,l,r;
}e[N<<1];
int ecnt,head[N];
inline void link(int u,int v,int l,int r){
    e[++ecnt]=(edge){v,head[u],l,r};
    head[u]=ecnt;
}
int n,m;
namespace SUB1_bl{//o(M*(N^2)):n,m<=20
    int now=0,lim;
    void dfs(int x,int f,int dis){
        if(dis>now)now=dis;
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].to,l=e[i].l,r=e[i].r;
            if(y==f||lim<l||lim>r)continue;
            dfs(y,x,dis+1);
        }
    }
    void solve(){
        while(m--){
            lim=read();now=0;
            for(R int i=1;i<=n;++i)dfs(i,0,0);
            printf("%d\n",now);
        }
    }
}
struct EE{
    int x,d;
}E[N];
vector<int>que[N];
int res[N];
inline bool cmpsub2(EE a,EE b){return a.d<b.d;}
namespace SUB2_linkandL{//O(NlogN+M) 所有l=1且形态为链 
    int ma[N],mi[N],CNT,tmp[N],bcj[N],ans=0;
    int find(int k){return bcj[k]==k?k:bcj[k]=find(bcj[k]);}
    void solve(){
        for(R int x=1;x<=n;x++){
            ma[x]=mi[x]=bcj[x]=x;
            for(R int i=head[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(y==x+1){
                    E[++CNT]=(EE){x,e[i].r};
                    tmp[CNT]=e[i].r;
                    break;
                }
            }
        }
        sort(E+1,E+CNT+1,cmpsub2);
        sort(tmp+1,tmp+CNT+1);
         
        for(R int i=1;i<=m;++i){
            int qnum=read();
            qnum=lower_bound(tmp+1,tmp+CNT+1,qnum)-tmp;
            que[qnum].push_back(i);
        }
         
        for(R int i=CNT;i>=1;i--){
            int u=E[i].x,v=u+1,A=find(u),B=find(v);
            if(A==B)continue;
            bcj[A]=B;
            ma[B]=max(ma[B],ma[A]);
            mi[B]=min(mi[B],mi[A]);
            ans=max(ans,ma[B]-mi[B]);
            for(int j=0;j<que[i].size();++j)res[que[i][j]]=ans;
        }
        for(R int i=1;i<=m;++i)printf("%d\n",res[i]);
     
    }
}
typedef pair<int,int> pii;
vector<pii>g[N];
namespace SUB3_LINK{//链的所有情况 
    struct SGT{
        int l,r;
        int S,li,ri;
        /*S:区间内最大连续链长 
          li:左端点向右的最大连续距离
          ri:右端点向左的最大连续距离 
        */
    }b[N<<2];
    void build(int o,int l,int r){
        b[o].l=l,b[o].r=r,b[o].S=b[o].li=b[o].ri=0;
        if(l==r)return;
        int mid=l+r>>1;
        build(o<<1,l,mid),build(o<<1|1,mid+1,r);
    }
    void up(int o){
        int ls=o<<1,rs=o<<1|1;
        int sizL=b[ls].r-b[ls].l+1,sizR=b[rs].r-b[rs].l+1;
        b[o].S=max(max(b[ls].S,b[rs].S),b[ls].ri+b[rs].li);
        b[o].li=b[ls].li;
        if(b[ls].li==sizL)b[o].li+=b[rs].li;
        b[o].ri=b[rs].ri;
        if(b[rs].ri==sizR)b[o].ri+=b[ls].ri;
    }
    void update(int o,int pos,int z){
        if(b[o].l==b[o].r){
            b[o].S=b[o].li=b[o].ri=z;
            return;
        }
        int mid=b[o].l+b[o].r>>1;
        if(pos<=mid)update(o<<1,pos,z);
        else update(o<<1|1,pos,z);
        up(o);
    }
    void solve(){
        for(R int x=1;x<n;x++){
            for(R int i=head[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(y==x+1){
                    g[e[i].l].push_back(make_pair(x,1));
                    g[e[i].r+1].push_back(make_pair(x,0));
                    break;
                }
            }
        }
        for(R int i=1;i<=m;i++){
            int x=read();
            que[x].push_back(i);
        }
        build(1,1,n-1);//n-1条线段 
        for(R int i=1;i<=n;i++){
            for(R int j=0;j<g[i].size();j++)update(1,g[i][j].first,g[i][j].second);
            for(R int j=0;j<que[i].size();j++)res[que[i][j]]=b[1].S;
        } 
        for(R int i=1;i<=m;i++)printf("%d\n",res[i]);
    }
}
bool nc2;
int main(){
    n=read(),m=read();
    bool islink=1,Lequal1=1;
    for(int i=1;i<n;++i){
        int u=read(),v=read(),l=read(),r=read();
        link(u,v,l,r);link(v,u,l,r);
        if(u!=v+1&&v!=u+1)islink=0;
        if(l!=1)Lequal1=0;
    }
    if(n<=20){SUB1_bl::solve();return 0;}
    if(islink&&Lequal1){SUB2_linkandL::solve();return 0;}
    if(islink){SUB3_LINK::solve();return 0;}
}

Substack4: l = 1 in all cases

Only consider limiting of r.

Before NOIP Zhenti have done similar, it seems that this problem NOIP2013 trucking . Trucking This question is for you bearing the weight of each route, a number of inquiries, ask you the maximum number of goods can be brought from u to v. Practice is off-line inquiries , from the widest limit (maximum weight bearing) side began to increase, then the tree for the new generation of heuristic merge , maintain two-point connectivity, the way to update the answer.

Two connectivity is not difficult to maintain, but that the need to maintain a new tree diameter .

Substack2 still based on the idea, but now the new tree diameter is not a long chain. For the current edge \ ((X, Y) \) , if you have a tree belonging to the same continue to fall, and vice versa for the merging of two trees belong to, and seek new merge tree diameter.

Each communication block (tree), maintaining two things { \ (A [], B [] \) }, respectively, the communication block (tree) of the two end diameters .

For the merged new tree, it is easy to know, may have only a diameter of a new \ (C_4 ^ 2 = 6 \ ) cases, i.e. the length of two diameters selected as an end point of the new tree, the comparison is formed in the original four terminals, take six species longest one update. How to quickly seek two points from it? In fact, it is equal to the distance (on all sides are connected) original tree, the tree with a cross-sectional / LCA multiplier determined directly from the can and then find the original tree.

Time complexity is \ (O (NlogN M +) \) , combined Substack1, Substack3 score 80 is desirable.

Substack5: All cases

Now also consider the limitations of the l :(

Since the vehicle speed is in (\ [1, n]) \ room, considering all the pre-finished \ (ans [i = 1..n] \) represents the vehicle speed is i can go the longest path and answer questions directly online .

The vehicle speed considerations partition . For the current interval \ ([L, R & lt] \) , the road speed limit is set \ ([QL, QR] \) . All will meet \ (ql <= l, r <= qr \) exists in the way this speed range (indicates that the road speed limit is entirely contained in this section). Like this:

//当前存的道路限速为[ql,qr],当前车速区间为[l,r],节点编号为o
//u,v是道路的两个端点
int head[N<<2],U[N*20],V[N*20],nxt[N*20],ecnt;
//大概N<<2个节点,大概会存N*logN次边
void Insert(int o,int l,int r,int ql,int qr,int u,int v){
    if(ql<=l&&r<=qr){
        U[++ecnt]=u;V[ecnt]=v;
        nxt[ecnt]=head[o];head[o]=ecnt;//前向星存边 
        return;
    }
    int mid=l+r>>1;
    if(ql<=mid)Insert(o<<1,l,mid,ql,qr,u,v);
    if(qr>mid)Insert(o<<1|1,mid+1,r,ql,qr,u,v);
}

First to analyze the complexity of just this side of the memory, m edges each side are once stored, the number of layers of the recursive \ (logN \) , so \ (O (MlogN) \) . Incidentally, note the deposit side of the array size.

After a deposit side began to divide and conquer even the edges.

//当前节点为o,速度区间为[l,r],ret表示此时的树上最大链长
void solve(int o,int l,int r,int ret){
    int pos=cur;//cur和pos的具体用处下面会讲到
    for(R int i=head[o];i;i=nxt[i])merge(U[i],V[i],ret);
    //merge(u,v,ret)表示连接uv这条边,并更新当前ret
    if(l==r){
        ans[l]=ret;//当递归到叶子时得到ans
        Retrace(pos);//Retrace:回溯
        return;
    }
    int mid=l+r>>1;
    solve(o<<1,l,mid,ret);
    solve(o<<1|1,mid+1,r,ret);
    Retrace(pos);//分治到另一半时还得把当前这一半的影响给回溯了
}

Specific merge function to achieve speak with Substack4 in essentially the same, using the disjoint-set to maintain connectivity relationships (but not the path compression , because even back then) and the diameter of the current two endpoints in the tree A[i],B[i], then direct enumeration \ (C_4 ^ 6 = 2 \) cases, and to update diameter end of the new tree.

code show as below:

int Find(int x){return fa[x]==x?x:Find(fa[x]);}
inline void Dia(int a,int b,int &P1,int &P2,int &ma){//考虑(a,b)作为新树直径的情况 
    int tmp=dep[a]+dep[b]-2*dep[LCA(a,b)];//这个LCA在前面预处理出来,倍增或树剖
    if(tmp>ma)ma=tmp,P1=a,P2=b;//更新直径长度及两个端点    
}
inline void merge(int x,int y,int &ret){//在(x,y)间连边,形成新树 
    x=Find(x),y=Find(y);
    int P1,P2,ma=-1,tmp;
    //六种情况
    Dia(A[x],B[x],P1,P2,ma);
    Dia(A[x],A[y],P1,P2,ma);
    Dia(A[x],B[y],P1,P2,ma);
    Dia(B[x],A[y],P1,P2,ma);
    Dia(B[x],B[y],P1,P2,ma);
    Dia(A[y],B[y],P1,P2,ma);    
    if(ma>ret)ret=ma;
    //!!!!
    if(rk[x]==rk[y]){//当两树秩相等时,将Treey连到Treex上,Treex的秩++ 
        rk[x]++;
        op[++cur]=(Op){0,x,0};
    }
    if(rk[x]<rk[y])swap(x,y);   
    
    op[++cur]=(Op){1,y,0};
    op[++cur]=(Op){2,x,A[x]};
    op[++cur]=(Op){3,x,B[x]};
    fa[y]=x,A[x]=P1,B[x]=P2;
    //!!!!
}

See the following section marked emphasis, in fact, this part by rank merge two trees, and prepare for operation after backtracking .

By rank merged part is easy to understand, is the height (rank) and even at the height of a small tree (rank) larger trees. Preparing to back the code can be combined with the following code to understand backtracking.

This structure op operation history window, a similar stack form to store back operation, curit means that the current element in the stack (requires backtracking operation). Partition function before can now explain the solve()significance of this code of int pos=cur;//cur和pos的具体用处下面会讲到its storage location of the front recursive subtree top of the stack, and now I go back to the partition only pos position to the other half of the time.

struct Op{//记录回溯操作 
    int t,x,y;
}op[N<<2];
inline void Retrace(int pos){//回溯到pos位置 
    while(cur>pos){
        if(op[cur].t==0)rk[op[cur].x]--;
        if(op[cur].t==1)fa[op[cur].x]=op[cur].x;
        if(op[cur].t==2)A[op[cur].x]=op[cur].y;//还原该树直径端点1 
        if(op[cur].t==3)B[op[cur].x]=op[cur].y;//还原该树直径端点2 
        cur--;
    }
}

So that the entire practice is over, attention to detail specific implementation, including the size of the array / array name confusion / path can not compress them.

The above appears to be very violent divide and conquer approach to analyze the complexity, while stored \ (MlogN \) times, each side are up merge()and out of the stack once, find and check on the consideration set, the entire time complexity of the algorithm approximately \ (O (Nlog ^ 2N) \) .

The complete code is as follows:

#include<bits/stdc++.h>
#define R register
using namespace std;
const int N=70010;
int n,m,cur,ans[N];
int sz[N],f[N],dep[N],son[N],top[N];//树剖 
int fa[N],rk[N],A[N],B[N];//并查集 

int head[N<<2],U[N*20],V[N*20],nxt[N*20],ecnt;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
struct Op{//记录回溯操作 
    int t,x,y;
}op[N<<2];
vector<int>g[N];
void dfs(int x){
    sz[x]=1;
    for(int i=0;i<g[x].size();++i){
        int y=g[x][i];if(y==f[x])continue;
        f[y]=x,dep[y]=dep[x]+1;
        dfs(y),sz[x]+=sz[y];
        if(sz[y]>sz[son[x]])son[x]=y;
    }
}
void redfs(int x,int tp){
    top[x]=tp;
    if(son[x])redfs(son[x],tp);
    for(int i=0;i<g[x].size();++i){
        int y=g[x][i];if(y==f[x]||y==son[x])continue;
        redfs(y,y);
    }
}
inline int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=f[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
int Find(int x){return fa[x]==x?x:Find(fa[x]);}
inline void Dia(int a,int b,int &P1,int &P2,int &ma){//考虑(a-b)作为新树直径的情况 
    int tmp=dep[a]+dep[b]-2*dep[LCA(a,b)];
    if(tmp>ma)ma=tmp,P1=a,P2=b; 
}
inline void merge(int x,int y,int &ret){//在(x,y)间连边,形成新树 
    x=Find(x),y=Find(y);
    int P1,P2,ma=-1,tmp;
    Dia(A[x],B[x],P1,P2,ma);
    Dia(A[x],A[y],P1,P2,ma);
    Dia(A[x],B[y],P1,P2,ma);
    Dia(B[x],A[y],P1,P2,ma);
    Dia(B[x],B[y],P1,P2,ma);
    Dia(A[y],B[y],P1,P2,ma);    
    if(ma>ret)ret=ma;
    
    if(rk[x]==rk[y]){//当两树秩相等时,将Treey连到Treex上,Treex的秩++ 
        rk[x]++;
        op[++cur]=(Op){0,x,0};
    }
    if(rk[x]<rk[y])swap(x,y);   
    op[++cur]=(Op){1,y,0};
    op[++cur]=(Op){2,x,A[x]};
    op[++cur]=(Op){3,x,B[x]};
    fa[y]=x,A[x]=P1,B[x]=P2;
}
inline void Retrace(int pos){//回溯 
    while(cur>pos){
        if(op[cur].t==0)rk[op[cur].x]--;
        if(op[cur].t==1)fa[op[cur].x]=op[cur].x;
        if(op[cur].t==2)A[op[cur].x]=op[cur].y;//还原该树直径端点1 
        if(op[cur].t==3)B[op[cur].x]=op[cur].y;//还原该树直径端点2 
        cur--;
    }
}
void Insert(int o,int l,int r,int ql,int qr,int u,int v){
    if(ql<=l&&r<=qr){
        U[++ecnt]=u;V[ecnt]=v;
        nxt[ecnt]=head[o];head[o]=ecnt;//存边 
        return;
    }
    int mid=l+r>>1;
    if(ql<=mid)Insert(o<<1,l,mid,ql,qr,u,v);
    if(qr>mid)Insert(o<<1|1,mid+1,r,ql,qr,u,v);
}
void solve(int o,int l,int r,int ret){
    int pos=cur;
    for(R int i=head[o];i;i=nxt[i])merge(U[i],V[i],ret);
    if(l==r){
        ans[l]=ret;
        Retrace(pos);
        return;
    }
    int mid=l+r>>1;
    solve(o<<1,l,mid,ret);
    solve(o<<1|1,mid+1,r,ret);
    Retrace(pos);
}
int main(){
    n=read(),m=read();
    for(R int i=1;i<n;++i){
        int u=read(),v=read(),l=read(),r=read();
        g[u].push_back(v);
        g[v].push_back(u);
        Insert(1,1,n,l,r,u,v);
    }
    dfs(1);redfs(1,1);
    for(R int i=1;i<=n;++i)fa[i]=A[i]=B[i]=i;
    solve(1,1,n,0);
    while(m--)printf("%d\n",ans[read()]);
}

Guess you like

Origin www.cnblogs.com/Tieechal/p/11648640.html