問題の木

タスク1

重力ツリーの定義された中央:この時点までは、ルート、そのサブツリーのすべての後、サイズツリー全体の半分以下です。

まず、焦点はリーフノード自身でなければなりません。
ノードuの最も重い息子vを考えてみましょう、それは究極の答えは(外サブツリーにジャンプしない)息子の重心にいくつかのステップをジャンプアップしなければならない明確です

重力の中心の存在のために、それが唯一のサブツリー全体缶を超えていないノードと電流の息子サブツリーの数の半分を満たすために必要はありません。

# include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
struct rec{ int pre,to;}a[N<<1];
int head[N],fa[N],ans[N],size[N];
int n,m,tot;
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs1(int u,int f)
{
    fa[u]=f; size[u]=1;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==f) continue;
        dfs1(v,u); size[u]+=size[v];
    }
    if (size[u]==1) ans[u]=u;
}
void dfs2(int u,int f)
{
    if (size[u]==1) return;
    ans[u]=u; int ret=0;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==f) continue;
        dfs2(v,u);
        if (size[v]>size[ret]) ret=v;
    }
    if (size[u]<(size[ret]<<1)) {
        int v=ans[ret];
        while (((size[u]-size[v])<<1)>size[u]) v=fa[v];
        ans[u]=v;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=2;i<=n;i++) {
        int t; scanf("%d",&t);
        adde(i,t); adde(t,i);
    }
    dfs1(1,0);

    dfs2(1,0);
    for (int i=1;i<=m;i++) {
        int x; scanf("%d",&x);
        printf("%d\n",ans[x]);
    }
    return 0;
}

タスク2

SQRT(N)アプローチnを考えます。
ルートノードへのパス上の各ノードの数を記録数に関する情報は、接頭辞は、係数情報変化点の答えを更新しています。
まだ少しアップデートは:追加は再びそれを行う、現在のポイントを削除します。

情報を消去したとき、私は戻って覚えています。

しかし、これは、迅速に、直接暴力+剪定しません...

これは、TLEの標準的なプロセスであります

# include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct rec{ int pre,to;}a[N<<1];
int w[N],head[N],n,cnt,t[N],ans[N],tot;
map<int,int>mp;
vector<int>pp[N];
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs1(int u,int fa,int L)
{
    ans[u]=L;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dfs1(v,u,gcd(L,w[u]));
    }
}
void dfs2(int u,int fa,int dep)
{
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        for (int j=0;j<pp[v].size();j++) {
            mp[pp[v][j]]++;
            if (mp[pp[v][j]]>dep) ans[v]=max(ans[v],pp[v][j]);
        }
        dfs2(v,u,dep+1);
        for (int j=0;j<pp[v].size();j++) mp[pp[v][j]]--;
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=1;i<=n-1;i++) {
        int u,v; scanf("%d%d",&u,&v);
        adde(u,v); adde(v,u);
    }
    dfs1(1,0,0);
    for (int i=1;i<=n;i++) {
        int x=w[i];
        for (int j=1;j<=sqrt(x);j++)
        if (x%j==0) {
            pp[i].push_back(j);
            if (x/j!=j) pp[i].push_back(x/j);
        }
    }
    for (int i=0;i<pp[1].size();i++) mp[pp[1][i]]++;
    ans[1]=max(ans[1],w[1]);
    dfs2(1,0,0);
    for (int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

ここでは、その後、奇妙な剪定ACプログラムは次のとおりです。

# include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
struct rec{
    int pre,to;
}a[N<<1];
int tot,n;
int ans[N],w[N],dis[N],head[N];
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs(int u,int fa)
{
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        ans[v]=dis[v]=gcd(dis[u],w[v]);
        dfs(v,u);
    }
}
void work(int u,int fa,int d)
{
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        int t=gcd(d,w[v]);
        if (ans[v]%t==0) continue;
        ans[v]=max(ans[v],t); work(v,u,t);
    }
}
void dfs2(int u,int fa)
{
    ans[u]=max(ans[u],dis[fa]);
    work(u,fa,dis[fa]);
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dfs2(v,u);
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=2;i<=n;i++) {
        int u,v; scanf("%d%d",&u,&v);
        adde(u,v); adde(v,u);
    }
    ans[1]=dis[1]=w[1];
    dfs(1,0); dfs2(1,0);
    for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}

タスク3

どうやらアリスの最適パスは徐々にボブに押され、
そしてボブは父親の方向に逃げるかもしれないが、ノードに沿って実行しているノード最長のチェーンを選択します。

セット\(C_ {U}が\)(U含む)U以下ノードの最大鎖長を表します。

もちろん、それは、その最長のチェーン実行には、次のノードに正当なものである満たすためになりました。

場合にのみ、\(dep_ {X} - dep_
{U}> dep_ {U} -dep_ {1} \) のすべての法的点Uのために、我々は、計算された2つの人の操作の最大数を必要とします。

元の位置は、1回の操作で固定されないでそれはここで停止留意、
すなわち$ 2 \回(dep_u - dep_1最大限 + C_ {U} -1)$を

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=4e5+10;
struct rec{ int pre,to;}a[N<<1];
int n,x,head[N],tot,c[N],dep[N],f[N];
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs(int u,int fa)
{
    int mx=0;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dep[v]=dep[u]+1; f[v]=u; dfs(v,u); mx=max(mx,c[v]);
    }
    c[u]=1+mx;
}
signed main()
{
    scanf("%lld%lld",&n,&x);
    for (int i=1;i<n;i++) {
        int u,v; scanf("%lld%lld",&u,&v);
        adde(u,v); adde(v,u);
    }
    dfs(1,0);
    int y=x,ans=0;
    do {
        if (dep[x]-dep[y]<dep[y]-dep[1]) 
         ans=max(ans,2*(dep[y]-dep[1]+c[y]-1));
        y=f[y];
    }while (y!=1);
    if (dep[x]-dep[y]<dep[y]-dep[1]) ans=max(ans,2*(dep[y]-dep[1]+c[y]-1));
    printf("%lld\n",ans);
    return 0;
 }

タスク4

ノードが削除された場合の答えの寄与の大きさであるそのサブツリーを、取らなければならないすべてを削除し、リーフノードを削除するためには、考えてみましょう。

プレフィックスの最大のDFSイベントのコース及び右側に0未満であり、その後、電流経路の一端から副ルートノードとして、すなわち、経路長が起動現在のノード・レコードに相当する、0に割り当てられたままであります連続サブセグメント、及び(貪欲選択)。

このような複雑さは$ O(n)は$です

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct rec{ int pre,to,w;}a[N<<1];
int lim[N],n,head[N],tot,ans;
void adde(int u,int v,int w)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    a[tot].w=w;
    head[u]=tot;
}
void dfs(int u,int fa,int L)
{
    if (lim[u]<L) return;
    ans++;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dfs(v,u,max(L+a[i].w,0));
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&lim[i]);
    for (int i=2;i<=n;i++) {
        int u=i,v,w; scanf("%d%d",&v,&w);
        adde(u,v,w); adde(v,u,w);
    }
    dfs(1,0,0);
    printf("%d\n",n-ans);
    return 0;
}

タスク5

非常に明確に、点のセットが必要になることがあり、そして全ての点が最も遠い点までの距離よりも小さいDまたは以下とすることができる点のセットのみDに等しく、この時点です。

最遠点のセットポイントへのポイントのために遠く離れて1〜2点の2点を中心にしておく必要があります。

最遠点は、2つのポイントの一つではない場合、それは必然的に最遠点を介して相互につながるパスの最も遠い点は、悪魔のドット径に矛盾に、長いオリジナルよりも直径で構成されています。

2つの遠く離れてマークポイントを見つける方法。

DFSは2回使用することができます。

次に、DFSを見つけるために、二つの点から始まる最大を取ります。

次いで、全ての点を脇に掃引し、答えがDに等しい未満である場合、回答に含まれます。

複雑\(O(n)は、\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct rec{
    int pre,to;
}a[N<<1];
bool mark[N];
int n,m,lim,tot,head[N],d[N];
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs1(int u,int fa)
{
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        d[v]=d[u]+1;
        dfs1(v,u);
    }
}
void dfs2(int u,int fa,int step)
{
    d[u]=max(d[u],step);
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dfs2(v,u,step+1);
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&lim);
    memset(mark,false,sizeof(mark));
    for (int i=1;i<=m;i++) {
        int t; scanf("%d",&t); mark[t]=true;
    }
    for (int i=2;i<=n;i++) {
        int u,v; scanf("%d%d",&u,&v);
        adde(u,v); adde(v,u);
    }
    memset(d,0,sizeof(d)); d[0]=-1;
    dfs1(1,0); int pt1=0; 
    for (int i=1;i<=n;i++) if ((d[i]>d[pt1])&&mark[i]) pt1=i;
    memset(d,0,sizeof(d)); d[0]=-1;
    dfs1(pt1,0);int pt2=0;
    for (int i=1;i<=n;i++) if  ((d[i]>d[pt2])&&mark[i]) pt2=i;
    memset(d,0,sizeof(d)); d[0]=-1;
    dfs2(pt1,0,0);dfs2(pt2,0,0);
    int ans=0;
    for (int i=1;i<=n;i++) if (d[i]<=lim) ans++;
    printf("%d\n",ans);
    return 0;
}

タスク6

明らかに、2つの非常に特別な事情があるだろう、つまり、

  • N = 1、答えは1(染色鎖状による)であります
  • N = 2の回答2(染色チェーンの形状に応じて)
  • N> 2の場合答え3の下限

染色、インペリアルノード1、およびそのノードの息子染色(現在の基準点と色父の現在のポイント、息子が番号を繰り返さない)場合、各ノードに1つのノードトラバーサルから始まります

# include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct rec{
    int pre,to;
}a[N<<1];
int head[N],tot,n,k,col[N]; 
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs(int u,int fa)
{
    int num=1;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        while (col[u]==num||col[fa]==num) num++;
        col[v]=num++;  k=max(k,col[v]);
        dfs(v,u);
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++) {
        int u,v; scanf("%d%d",&u,&v);
        adde(u,v); adde(v,u);
    }
    col[1]=1; dfs(1,0);
    printf("%d\n",k);
    for (int i=1;i<=n;i++) printf("%d ",col[i]);
    puts("");
    return  0;
}

タスク7

1 =すべてのルートを取る
の回答のルートにこの時点まで、各ポイントの寄与を検討します。

エッジ重みので(0 \> W)\のでプレフィックスと単調、特定の臨界点乗算見出すことができる\(Xを\)だけ現在のポイントに\(U \)距離以下\(a_v \) 次いで結合しました、\ (X- \)する\(父親(U)を\)チェーン内のすべての点では、答えと一致するだろう。

加算器を維持するパスのみを考慮、各エンドノードの特定の値を尋ねます。

ツリーは差分を解決するために使用することができます。

メンテナンス右パス加算器、\(U、V \)チェーン動作は、\(d_u + = 1 \) \(D_v + = 1 \) \(D_ {LCA(U、V)} - = 1、D_ {父(LCA(U、V))} - = 1 \)

そして、最終的にはストレートサブツリーを実行し、元の木です。

複雑\(O(N \ログの\ n)は、\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e5+10;
struct rec{
    int pre,to,w;
}a[N<<1];
int tot,n;
int head[N],c[N],g[N<<1][20],d[N<<1][20],w[N];
void adde(int u,int v,int w)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    a[tot].w=w;
    head[u]=tot;
}
void dfs(int u,int fa)
{
    g[u][0]=fa;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        d[v][0]=a[i].w; dfs(v,u);
    }
}
void init()
{
    dfs(1,0);
    for (int i=1;i<=18;i++)
     for (int j=1;j<=n;j++)
      g[j][i]=g[g[j][i-1]][i-1],
      d[j][i]=d[j][i-1]+d[g[j][i-1]][i-1];
}
int jump(int u,int sum)
{
    for (int i=18;i>=0;i--)
     if (d[u][i]<=sum&&g[u][i]!=0) { sum-=d[u][i]; u=g[u][i];}
    return u;  
}
void dfs2(int u,int fa)
{
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dfs2(v,u); c[u]+=c[v];
    }
}
signed main()
{
    scanf("%lld",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&w[i]);
    for (int i=2;i<=n;i++) {
        int u=i,v,w; scanf("%lld%lld",&v,&w);
        adde(u,v,w); adde(v,u,w);
    }
    init();
    for (int i=1;i<=n;i++) {
        int to=jump(i,w[i]); 
        c[g[to][0]]--; c[g[i][0]]++;
    }
    dfs2(1,0);
    for (int i=1;i<=n;i++) printf("%lld ",c[i]); puts("");
    return 0;
}

おすすめ

転載: www.cnblogs.com/ljc20020730/p/11291471.html