ダッシュ速度[良い質問、パーティションは、マージランク互いに素設定]

ダッシュ速度

裁判官オンライン:NOIP2016 10共同測定、クラリス#2 T3

ラベル:いい質問、パーティション、ランクによってばらばらセットをマージし、LCA

タイトル説明

聖なる山のレースをドラッグして、町のビットのビットです。直接的または間接的に、ツリー構造を形成するために一緒に接続された双方向私道 - 丘内のNビットを順次1からnまでの番号正方形、Nによって正方形の間があります。

工事用品や時間の各レーンが異なっているので、車がこのレーン過去の里李速度よりもより少ないとないではない場合にのみ、量子化間隔は、1つのレーンを負担表し里、2桁の李を使用することが可能ですときに路面に損傷が発生することはありません。

Byteasarは最近、スポーツカーを買って、彼は少し山は車を急騰したかったのです。Byteasarは、異なる2点のS、Tを選択し、最短経路木の上にそれらを実行し、上記1車線のいずれかに害を与えないようにする予定です。

Byteasar速度を変更するために好きではないので、彼はあなたに彼のスピードを教えてくれます。最適な速度を選択するためには、Byteasarの合計がm回にお聞きします。できるだけ多くのパス上のレーンを通じて多数のように、彼は合法的な方法を見つける助けてください。

エントリー

最初の行は2つの正の整数N、M、及び二乗問い合わせ図の総数の合計数を含んでいます。

次に、N - 1行は、4つの正の整数UIの各行は、VI、リチウム、RI、VIおよびUIが接続する2つのレーンを表し、区間[リチウム、RI]を受信します。

次のm行の各正の整数チー、それぞれ、車速の各質問。

輸出

最長経路チーの長さは、有効なパス0が出力されない場合、前記i行目の出力速度Mの出力線は、それぞれ整数、。

サンプル

入力#1

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

出力#1

0
2
3

サンプル説明:
車両1が正当である場合にパスが存在しません。
車速が2である場合、この経路長が2である1-5-4選択することができます。
3車速がこの経路長3-2-1-5から選択される3であります

問題の解決策

Substack1:暴力

各問い合わせ直接列挙エンドポイントの場合は、最大距離を探して、再びDFS。

時間計算量は\(O(M \ CDOT N ^ 2)\) スコア20が望ましいです。

Substack2:チェーンのツリー分解及びL = 1

Lは制限されないので、限り、以上の現在のエッジrはリムを制限するように、それが渡されます。検討オフラインクエリ、Rのキーワードとソートすべてのエッジ互いに素なセットブロックとの通信を維持し、問い合わせを接続、通信レコード2つの値の各ブロック\(MA、MIザ\)通信ブロックノードの最大/最小を表し、鎖長の深さは、現在のブロックは、通信用に構成されている\(MA-MI \)を

時間計算量は、(O(NlogN M +)\)\、組み合わせSubstack1におけるスコア40が所望の。

Substack3:すべての場合にツリーチェーン縮退

比較するとSubstack2は、ここにもリットルを制限することを検討して。

現在列挙車速を、制限速度が側{ある場合\(L、R&LTの\) }、その後、車速がさ(Lの\)を\車速がされた場合でも、このエッジを加えた(R + 1 \)\削除各車速に対する回答のためにも、このエッジ、形成されたエッジの全ての偶数鎖長の最大電流

メンテナンスは、使用することができ、ツリーラインを次のようにメンテナンス特定ツリーセグメントの各ノードについて、の範囲内のツリーの対応する側に設けられた\()(L、R&LT \) 三つの値を維持する\((リー、RI、S)\)、\ (リー\)を表し\(Lの\)延びる右長に、最大連続電力を\(RI \)を表し\(R&LT \)最大連続長の左側には、拡張することができる\(S \)は区間内の最大鎖長を表します。 。

時間計算量は、(O(NlogN M +)\)\、組み合わせSubstack1におけるスコア60が所望の。

次のように最初の3つのコードの場合をスライスすることです。

#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、すべての場合において

Rのみの限定を検討してください。

NOIP Zhenti前に、それはこの問題と思われ、同様の行っているNOIP2013のトラック輸送トラック運送この質問はあなたが、問い合わせの数を各ルートの重量を支持するためのもので、商品の最大数は、uからvへもたらすことができるかを尋ね。練習はオフライン問い合わせ、最も広い限界(最大体重負荷)から側は新世代のツリーその後、増加し始めヒューリスティックマージ、二点接続、回答を更新する方法を維持します。

二つの接続は維持することは困難ではありませんが、維持する必要があることが新たに木の直径を

Substack2はまだ考えに基づいて、今新しいツリー径は、長鎖ではありません。現在のエッジの場合は\((X、Y)\) あなたが同じに属するツリーは低下し続けている場合、および2つのツリーのマージのためにその逆はに属し、新しいマージツリーの直径を求めます。

二つのことを維持し、各通信ブロック(木)、{ \(A []、B [] \) }、それぞれの通信ブロック(ツリー)両端直径

マージされた新しいツリーのために、知っていることは容易であり、新規の直径のみ有していてもよい\(= 6 C_4 ^ 2 \ ) の場合、新しいツリーの終点として選択2つの直径の、すなわち長さを、比較は、元の4つの端子に形成されています6種最長1つのアップデートを取ります。どのようにすぐにそれから2点を模索するには?実際には、元のツリーを見つけ、次に距離(すべての側面に接続されている)は、元のツリー、缶から直接決定断面/ LCAの乗算器とツリーに等しく、。

時間複雑である(O(NlogN M +)を\)\、合わせSubstack1、Substack3 80のスコアが望ましいです。

Substack5:すべての例

今もリットルの限界を考慮:(

車速が中にあるので([1、N] \) \の全ての前の完成を考慮して、部屋\(ANS [I = 1..nの] \) 車両速度を表し、私が最も長いパスを移動して、オンラインで直接質問に答えることができています。

車速考慮事項パーティション現在の間隔のための\([L、R&LT] \) 道路の制限速度が設定されている\([QL、QR] \) すべてが合致する(QL <= L、Rは\ <= QR \) 、この速度範囲は、(道路の制限速度が完全にこのセクションに含まれていることを示す)ように存在します。このように:

//当前存的道路限速为[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);
}

メモリのちょうど手前の複雑さを分析する最初の、mが一旦記憶されるそれぞれの側縁、再帰の層の数\(logN個\) そう\(O(MlogN)\) なお、アレイサイズの入金側に注意してください。

入金側の後にもエッジを分割し、征服し始めました。

//当前节点为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);//分治到另一半时还得把当前这一半的影响给回溯了
}

特定のマージ(ただし、接続関係を維持するためにばらばらのセットを使用して、本質的に同じでSubstack4と話す達成するための機能パス圧縮をしても当時ので、)とツリー内の現在の2つのエンドポイントの直径A[i],B[i]、その後、直接列挙\(C_4 ^ 6 = 2 \)の場合は、新しいツリーの直径端部を更新します。

コードは以下の通りであります:

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;
    //!!!!
}

以下のセクションでは、重点をマーク参照してください、実際には、この部分のランクによっては、マージ 2つのツリーを、そしてバックトラック後の動作のための準備します

ランクが一部が理解しやすい合併することで、高さ(ランク)と小さな木(ランク)大きな木の高さです。コードをバックアップするための準備がバックトラックを理解するために、次のコードと組み合わせることができます。

この構造OP操作履歴ウィンドウ、同様のスタック操作をバック格納する形態、curそれはスタック内の現在の要素が(バックトラック操作を必要とする)ことを意味します。パーティション関数は、前に、今説明することができsolve()、このコードの重要性int pos=cur;//cur和pos的具体用处下面会讲到スタックの前再帰サブツリーのトップのその保管場所を、そして今、私は時間の残りの半分に戻ってパーティションのみのpos位置に戻ります。

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--;
    }
}

全体練習が終わったように、詳細特定の配列/配列名の混乱の大きさなどの実装、/パスへの注目がそれらを圧縮することはできません。

上記記憶されている間、非常に暴力的な分割をすることと複雑さを分析するアプローチを征服するように見える\(MlogN \)回、各サイドアップでありmerge()、一度スタックのうち、検索し、検討セットにアルゴリズムの全体の時間複雑性を確認約\(O(Nlog ^ 2N)\)

次のように完全なコードは次のとおりです。

#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()]);
}

おすすめ

転載: www.cnblogs.com/Tieechal/p/11648640.html