[HNOI 2014]羅世界樹バレーP3233

問題の説明

世界のツリーは、拡張その枝が全体の世界を形成し、非常に素晴らしい木です。ここでは、人種や生き物の多様性との生活、彼らは共同で、絶対公正かつ衡平な女神アリソンを信じて、自分の信条に、公正な世界は木が根本的な礎石の無限の、連続運転することができようにすることです。
木のフォーメーションを記述するために数学的モデルを使用することができます:世界のツリーは、民族的には1からnまでのn人種が番号が付けられ、地面に番号が1からnまでの集落に住んでいる、数はその集落の同じレース番号。いくつかの集落間の双方向の通りをリンクしている、道路の長さは1です。保証は互いに和解に達することができるすべてがツリー構造を形成するように接続手段、およびリングは発生しません。これは、2個の接続されたコロニーそれらの経路長との間の距離を定義し、例えば、道路の間の道路とB、BとCの間の決済が存在する場合、各道路の長さとが不可能を有するためリングは、とCとの間の水平距離は2です。
公正性の理由から、私は今年、世界樹の王は、認可メートル一時的な集落の手順で、[i]のレースが必要です。人種X(レース番号のX)、もしYレースのための手順の最近仮ルールに離れレースから、XのY手順で支配される(IF(yはコロニーの数は、手順の点で)より数yが最小仮手続きである、請求同一の決済の手順に一時的距離)。
手順の暫定規則で各年の任務は毎年の完了はどのように多くの(集落の手続きのルールも管理手順で受け付けます)レース管理されます後に王は、Q-年間で、知っていただきたいと思います。今、あなたのために知られている霊長類の知恵にこのタスク:プログラム猿。タスクバーを完了するために、王を助けてください。

入力形式

最初の行の正の整数n、レースの世界で木の数。
次NL行は、各列2つの正の整数x、yは、xはコロニーの間で決済を表し、yは二重長さ1は
道路。次に、正の整数qの挙動は、王は尋ねた年の数を表します。
次Qブロック、各2行:
私が1mの正の整数を作用最初のブロック[i]は、許可の手順におけるi番目の仮の数を表します。
i番目のブロックMの2行目[i]は正の整数H [L]、H [2 ]、...、H [mは[I]、 コロニー数を仮手順で許可されているように、互いに異なる保証(発現しました)。

出力フォーマット

出力線Q、I mの挙動を含む[i]は整数、j番目の行が(j = 1,2 ... ,, M [i])と認可集落のH [J]ののi番目の数を示しますレース運営の手続きの暫定ルールの数。

サンプル入力

10
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
5
2
6 1
5
2 7 3 6 9
1
8
4
8 7 10 3
5C
2 9 3 5 8

サンプル出力

1 9
3 1 4 1 1
10
1 1 3 5
4 1 3 1 1

解決

まず、モデルの問題を分析します。クエリq、毎回キーポイントmは、存在するが、\(\和{M [I ]} \) 、我々は仮想ツリーを考えることができる、比較的小さいです。臨界点での手続きの暫定規則、仮想ツリーの設立につきようになっています。

仮想ツリーがセットアップされた後、我々はそれについてどのように動的計画法を検討します。最も近いキーポイントの重要なポイントは、それ自体であることから、仮想ツリーは、明らかに、離れた最寄りのキーポイントからポイントが必要です。その後、他の点は予約のために、セット([i]は\に)\を表す\(iは\)原点復帰を支配し、木は、規制のアイデアを移動明確点である(\)は\値のいずれか、その息子更新またはその父親によって更新(つまり、ポイントの息子は、に近いか、小さい数字の点からの距離に所属します)。したがって、私たちは父親の息子と2つの動的プログラミング、更新に彼の息子を持つ父親、更新時間に分けることができます。更新順序によっては、DFS連続的に異なります。

その後、我々は、手続きのポイント数で各管轄権を必要とします。現在のキーポイントが属する仮想ツリーの手順で知られている情報がある、私たちの次のタスクは、情報と、ツリー内の他のノードに情報を得ることです。すでにチェーンと契約隣接する二つのキーポイントの間で、仮想ツリーの構造原理を更新するには、我々は今、このチェーンと統計の回答のうち、回復しようとしています。仮想ツリーの二つの隣接点(AB&\)\境界点が存在しなければならない、との間のリンク\(C \)を行う\(C \)復帰点以下([B] \)は\より準州、リターン([A] \)は\管轄。このカットオフポイントがなければならないことを考えることは容易(\ [A]に)\\(\ [B]に)必ずしもコースの中間点(中点\(\)\(B \) 間それ以外の場合は\(abが\) 同じポイントに属している必要があります。私たちは、直接一つ一つは、あなたは、乗算の方法を使用することができ、見つけることができません。それでは、どのようデマンドチェーン\((a、b)は\ ) それとの間に貢献?セット\(\)最初の息子\(X \) プロシージャは必ずしもではないので(仮想ツリー......)からチェーン、\(B \)する(C \)を\の間点とそのサブツリーは、すべてのある\([B]に\)の残りの部分([A] \)は\:式は、式で
[B] = +大きさにF [] + =サイズ[X] -size [C]、F [C] -size [B] \] \
前記\( F [i]が\)を表し\(Iは\)ノードの数が管轄、ポイント\(サイズを[I] \)としてルートノードを表す\(Iは\)サブツリーサイズ。

図から分かるように、上記のプロセスが生成する仮想キーツリーの寄与については説明しません。これらの点について特別には、彼らが他の手順で接続することができるということです。仮想ツリーのキーポイントのための\(U \)だけでなく、手続のサブツリーとAT (U \)\と点復帰([U]は\に)\管轄。したがって、設定\(残りの[U]は\)に初期化されている\(サイズ[U]は\)以上の処理において、各キー見つける\(A \) A \(X \) 原因\を(RESTを[A] - =サイズ[X] \) そして最終的には分離\(REST \)を加え回答蓄積します。

彼はとても長い間、最終的にはコードのための話しました----

コード

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define N 300002
using namespace std;
int head[N],ver[N*2],nxt[N*2],l;
int n,m,i,j,k,size[N],dep[N],fa[N][20],tim,dfn[N],x;
int a[N],tmp[N],to[N],s[N],t[N],top,cnt,rec[N],rest[N],f[N];
int read()
{
    char c=getchar();
    int w=0;
    while(c<'0'||c>'9') c=getchar();
    while(c<='9'&&c>='0'){
        w=w*10+c-'0';
        c=getchar();
    }
    return w;
}
void insert(int x,int y)
{
    l++;
    ver[l]=y;
    nxt[l]=head[x];
    head[x]=l;
}
void dfs(int x,int pre)
{
    dfn[x]=++tim;fa[x][0]=pre;
    size[x]=1;dep[x]=dep[pre]+1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y!=pre){
            dfs(y,x);
            size[x]+=size[y];
        }
    }
}
void init()
{
    dfs(1,0);
    for(int j=0;(1<<(j+1))<n;j++){
        for(int i=1;i<=n;i++){
            if(fa[i][j]==0) fa[i][j+1]=0;
            else fa[i][j+1]=fa[fa[i][j]][j];
        }
    }
}
int LCA(int u,int v)
{
    if(dep[u]>dep[v]) swap(u,v);
    int tmp=dep[v]-dep[u];
    for(int i=0;(1<<i)<=tmp;i++){
        if(tmp&(1<<i)) v=fa[v][i];
    }
    if(u==v) return u;
    for(int i=(int)log2(1.0*n);i>=0;i--){
        if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    }
    return fa[u][0];
}
int cmp(const int &x,const int &y)
{
    return dfn[x]<dfn[y];
}
int dis(int x,int y)
{
    return dep[x]+dep[y]-2*dep[LCA(x,y)];
}
void dfs1(int x)
{
    rec[++tim]=x;rest[x]=size[x];
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        dfs1(y);
        if(!to[y]) continue;
        int d1=dis(to[y],x),d2=dis(to[x],x);
        if(d1<d2||(d1==d2&&to[y]<to[x])||!to[x]) to[x]=to[y];
    }
}
void dfs2(int x)
{
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        int d1=dis(to[x],y),d2=dis(to[y],y);
        if(d1<d2||(d1==d2&&to[x]<to[y])||!to[y]) to[y]=to[x];
        dfs2(y);
    }
}
void dp(int a,int b)
{
    int x=b,mid=b;
    for(int i=18;i>=0;i--){
        if(dep[fa[x][i]]>dep[a]) x=fa[x][i];
    }
    rest[a]-=size[x];
    if(to[a]==to[b]){
        f[to[a]]+=size[x]-size[b];
        return;
    }
    for(int i=18;i>=0;i--){
        int nxt=fa[mid][i];
        if(dep[nxt]<=dep[a]) continue;
        int d1=dis(nxt,to[b]),d2=dis(nxt,to[a]);
        if(d1<d2||(d1==d2&&to[b]<to[a])) mid=nxt;
    }
    f[to[a]]+=size[x]-size[mid];
    f[to[b]]+=size[mid]-size[b];
}
int main()
{
    n=read();
    for(i=1;i<n;i++){
        int u,v;
        u=read();v=read();
        insert(u,v);insert(v,u);
    }
    init();
    m=read();
    for(i=1;i<=m;i++){
        memset(head,0,sizeof(head));
        memset(f,0,sizeof(f));
        memset(to,0,sizeof(to));
        memset(rest,0,sizeof(rest));
        tim=top=l=0;
        x=read();
        for(j=1;j<=x;j++){
            a[j]=read();
            to[a[j]]=tmp[j]=a[j];
        }
        sort(a+1,a+x+1,cmp);
        if(to[1]!=1) s[++top]=1;
        for(j=1;j<=x;j++){
            int lca=0;
            while(top){
                lca=LCA(a[j],s[top]);
                if(top>1&&dep[s[top-1]]>dep[lca]){
                    insert(s[top-1],s[top]);
                    top--;
                }
                else if(dep[s[top]]>dep[lca]){
                    insert(lca,s[top]);
                    top--;
                    break;
                }
                else break;
            }
            if(s[top]!=lca) s[++top]=lca;
            s[++top]=a[j];
        }
        while(top>1){
            insert(s[top-1],s[top]);
            top--;
        }
        dfs1(1);dfs2(1);
        for(j=1;j<=tim;j++){
            for(k=head[rec[j]];k;k=nxt[k]) dp(rec[j],ver[k]);
        }
        for(j=1;j<=tim;j++) f[to[rec[j]]]+=rest[rec[j]];
        for(j=1;j<=x;j++) printf("%d ",f[tmp[j]]);
        puts("");
    }
    return 0;
}
//P.S. 用轻重链剖分求LCA可以不用开O2。(O_O)

おすすめ

転載: www.cnblogs.com/LSlzf/p/10985732.html