スプリットは、同様の長鎖である\(\ RM {にDSU \ \ツリー} \) ことを除いて、通常のツリー鎖スプリット(スプリット重鎖)のように書かれたアルゴリズム、(\ \ RM {SIZ } \)に変更し、最大\(\ RM {DEP} \ ) 最大サブツリーの深さの問題に関連したいくつかの時間を最適化することができます。
プロパティ
1、鎖長との全て\(O(N)\)レベル
全ての点はそうである、唯一のそれは一度だけカウントされる長鎖である\(O(N)\)レベル
2、長鎖の長さが小さくない父親の息子の長鎖より
上記保持していない場合は、ポイントの息子の父親は、元と矛盾して、長鎖長くなるように選択することができます。
このプロパティは、当然の結果を有する:任意の点、それを\(K \)回の先祖がより大きいか必然的に等しく、長鎖\(K \) 、同様のことが判明しました。
図3に示すように、開始点までのジャンプは、スイッチング周波数が長鎖である\(O(\ SQRT N) \) レベル
性質\(2 \) 、各遷移が少ない最後の、即ち、最悪の場合のジャンプロープの長鎖長より長い鎖長ではありません\(1,2,3、\ cdots \) 、すなわちジャンプ\(O(\ sqrtのN) \) 回
実装と例
第一の重チェーンの長鎖分割は同様の分割をDFS達成。
void dfs1(int u,int fu)
{
for (int i=head[u];i;i=sq[i].nxt)
{
int v=sq[i].to;
if (v==fu) continue;
dfs1(v,u);
if (len[son[u]]<len[v]) son[u]=v;
}
len[u]=len[son[u]]+1;
}
前記\(\ LEN)を記録することである\(U \)長鎖\(U \)サブツリー長。
1、シーク\(K \)レベルの先祖
一般的な考え方は、乗算を行うことができるということである(nはO(nは\ログ\ \)) 前処理(O(\ nはログ)\ \) 非常に良い問い合わせ、ルックスが、あなたはより良い行うことができます。
長鎖分割を考慮し、それぞれの長鎖の長さ\(M \) 、最大で前処理チェーン上面(\ K)\祖先下方\(K \)重息子、その1の性質によって知られている\(O(N)\) 、我々は、各チャレンジは、この点にジャンプした後、\(x \)祖先の長鎖の鎖長ことを保証するレベルの先祖で\(> KX \) 、この連鎖の上の鎖の後にジャンプは、その答えは、前処理の結果に応じて決定されます。
チェーンの長さを確保するために、どのように\(> KX \)それ?各発見\(K \) 、最上位ビットのバイナリ表現を注意\(なH_k \)ビット、次いでせ\(X = 2 ^ {なH_kを } \) することができ、前処理と同じ倍加そうその合計のアレイである\(O(Nログ\ N )\) 前処理\(O(1)\)クエリ。
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=100000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u];i;i=sq[i].nxt)
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
struct node{int to,nxt;}sq[1001000];
int all=0,head[500500];
int n,dep[500500],mx[500500],son[500500],fa[500500][20],tp[500500],rt,hbit[500500],q;
vector<int> U[500500],D[500500];
inline int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
#define ui unsigned int
ui s;
inline ui get(ui x) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return s = x;
}
void add(int u,int v)
{
all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
}
void dfs1(int u,int fu)
{
dep[u]=dep[fu]+1;mx[u]=dep[u];fa[u][0]=fu;
rep(i,1,19) fa[u][i]=fa[fa[u][i-1]][i-1];
go(u,i)
{
int v=sq[i].to;
if (v==fu) continue;
dfs1(v,u);
if (mx[v]>mx[u]) {son[u]=v;mx[u]=mx[v];}
}
}
void dfs2(int u,int tpu)
{
tp[u]=tpu;
if (u==tpu)
{
int now=u;
rep(i,0,mx[u]-dep[u])
{
D[u].pb(now);
now=son[now];
}
now=u;
rep(i,0,mx[u]-dep[u])
{
U[u].pb(now);
now=fa[now][0];
}
}
if (son[u]) dfs2(son[u],tpu);
go(u,i)
{
int v=sq[i].to;
if ((v==fa[u][0]) || (v==son[u])) continue;
dfs2(v,v);
}
}
int query(int x,int k)
{
if (!k) return x;
x=fa[x][hbit[k]];k-=(1<<hbit[k]);
//cout << "half " << x << " " << k << endl;
k-=(dep[x]-dep[tp[x]]);x=tp[x];
//cout << "now " << x << " " << k << endl;
if (k>=0) return U[x][k];else return D[x][-k];
}
int main()
{
n=read();q=read();s=read();
rep(i,1,n)
{
int fa=read();
add(fa,i);add(i,fa);
if (!fa) rt=i;
}
rep(i,2,n) hbit[i]=hbit[i>>1]+1;
dfs1(rt,0);dfs2(rt,rt);
int ans=0;ll fin=0;
rep(i,1,q)
{
int x=(get(s)^ans)%n+1,k=(get(s)^ans)%dep[x];
ans=query(x,k);
fin^=(1ll*i*ans);
}
printf("%lld",fin);
return 0;
}
2、最適化特定のDP
フォームDPの一部状態\(F_ {U、I} \) 、\(I \)これだけの寸法と深さ。このようDPのために我々は、長鎖分割の使用を最適化することができます。具体的には、最初にやる\(U \)の息子と、この行く上記の回答のすべての合成光後の再息子を。合併時に我々は行うことができます場合は、\(O(LEN)\)合併は、合計時間の複雑さがある\(O(\ SUM LEN)\)で、\(O(n)は、\)
アップ暴力最初の書き込み方程式DP:\(F_ {U、I} \)を表し\(U \)距離サブツリー\(U \)のに等しい\(I \)次に、点の数持っています
\ [F_ {U、0} = 1、F_ {U、I} = \ sum_ {son_uにおけるV \} F_ {V、I-1} \]
その後、長鎖分割DPを最適化するために使用することができる第二の寸法と深さの情報のみ、
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=100000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u];i;i=sq[i].nxt)
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
struct node{int to,nxt;}sq[2002000];
int all=0,head[1001000];
int n,son[1001000],ans[1001000],*f[1001000],tmp[1001000],*id=tmp,len[1001000];
inline int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
void add(int u,int v)
{
all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
}
void dfs1(int u,int fu)
{
go(u,i)
{
int v=sq[i].to;
if (v==fu) continue;
dfs1(v,u);
if (len[son[u]]<len[v]) son[u]=v;
}
len[u]=len[son[u]]+1;
}
void dfs2(int u,int fu)
{
f[u][0]=1;
if (son[u])
{
f[son[u]]=f[u]+1;
dfs2(son[u],u);
ans[u]=ans[son[u]]+1;
}
go(u,i)
{
int v=sq[i].to;
if ((v==fu) || (v==son[u])) continue;
f[v]=id;id+=len[v];dfs2(v,u);
rep(j,1,len[v])
{
f[u][j]+=f[v][j-1];
if (((j<ans[u]) && (f[u][j]>=f[u][ans[u]])) || ((j>ans[u]) && (f[u][j]>f[u][ans[u]])))
ans[u]=j;
}
}
if (f[u][ans[u]]==1) ans[u]=0;
}
int main()
{
n=read();
rep(i,1,n-1)
{
int u=read(),v=read();
add(u,v);add(v,u);
}
dfs1(1,0);
f[1]=id;id+=len[1];
dfs2(1,0);
rep(i,1,n) printf("%d\n",ans[i]);
return 0;
}