zoukankan      html  css  js  c++  java
  • 向量

    题目描述

    给定一棵 $n$ 个节点的树,点的标号为 $1 dots n$ ,边有边权。
    记 $d(u, v)$ 为 $u$ 到 $v$ 的路径上边的权值和,对于每个节点 $u$ ,你需要给出一个 $m$ 维向量 $p_u = {p_{u,1}, dots, p_{u,m}}$ ,使得对于任意点对 $u,v$ ,满足 $d(u, v) = max{|p_{u,i} − p_{v,i}|}$ 。

    数据范围

    $2 le n le 1000$ ; $1 le w_i le 10^5$ ; $m le 16$

    题解

    怎么想到点分的不知道。

    考虑一个点分中心 $root$ ,把子树尽量按 $size$ 均分成两部分,然后使用一维向量,将 $A$ 集合的值设为 $deep$ , $B$ 集合设为 $-deep$ , $root$ 设为 $0$ ,然后将 $root$ 分别加入两边集合继续递归下去得到两个不同的向量,然后将其中一个向量平移成另一个向量即可,要把向量上所属集合也要平移。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1005,M=N<<1;
    int n,hd[N],V[M],nx[M],W[M],t,S[N],sz[N],rt;
    int o,vis[N],b[N],son[N],f[N][18],g[18][18];
    bool cmp(int x,int y){return sz[x]>sz[y];}
    void add(int u,int v,int w){
        nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;
    }
    void Sz(int x,int fr){
        sz[x]=1;
        for (int i=hd[x];i;i=nx[i])
            if (V[i]!=fr && !vis[V[i]])
                Sz(V[i],x),sz[x]+=sz[V[i]];
    }
    void Rt(int x,int fr){
        son[x]=o-sz[x];
        for (int i=hd[x];i;i=nx[i])
            if (V[i]!=fr && !vis[V[i]])
                Rt(V[i],x),son[x]=max(son[x],sz[V[i]]);
        if (son[x]<son[rt]) rt=x;
    }
    void push(int x,int fr,int v){
        vis[x]=v;
        for (int i=hd[x];i;i=nx[i])
            if (V[i]!=fr && !vis[V[i]])
                push(V[i],x,v);
    }
    void clr(int x,int fr,int v){
        vis[x]=0;
        for (int i=hd[x];i;i=nx[i])
            if (V[i]!=fr && vis[V[i]]==v)
                clr(V[i],x,v);
    }
    void gx(int x,int fr,int v){
        for (int i=v+1;i<=16;i++)
            f[x][i]+=g[v][i];f[x][v]=-f[x][v];
        for (int i=hd[x];i;i=nx[i])
            if (V[i]!=fr && !vis[V[i]])
                gx(V[i],x,v);
    }
    void down(int x,int fr,int v,int w){
        f[x][v]=w;
        for (int i=hd[x];i;i=nx[i])
            if (V[i]!=fr && !vis[V[i]])
                down(V[i],x,v,w+W[i]);
    }
    void work(int x,int v){
        o=sz[x];rt=t=0;Rt(x,0);x=rt;Sz(x,0);
        for (int i=hd[x];i;i=nx[i]) if (!vis[V[i]])
            S[++t]=V[i],down(V[i],x,v,W[i]);
        if (sz[x]<=2) return;
        sort(S+1,S+t+1,cmp);int w[2]={0,0};
        for (int j,i=1;i<=t;i++)
            j=w[0]>w[1],w[b[S[i]]=j]+=sz[S[i]];
        for (int i=hd[x];i;i=nx[i])
            if (!vis[V[i]] && b[V[i]]) push(V[i],x,v);
        sz[x]=w[0]+1;work(x,v+1);
        for (int i=v+1;i<=16;i++) g[v][i]=f[x][i],f[x][i]=0;
        for (int i=hd[x];i;i=nx[i]){
            if (!vis[V[i]]) push(V[i],x,v);
            else if (vis[V[i]]==v) clr(V[i],x,v);
        }
        sz[x]=w[1]+1;work(x,v+1);
        for (int i=v+1;i<=16;i++)
            g[v][i]-=f[x][i],f[x][i]+=g[v][i];
        for (int i=hd[x];i;i=nx[i])
            if (vis[V[i]]==v) clr(V[i],x,v);
            else if (!vis[V[i]]) gx(V[i],x,v);
    }
    int main(){
        cin>>n;son[0]=1e9;
        for (int i=1,x,y,z;i<n;i++)
            scanf("%d%d%d",&x,&y,&z),
            add(x,y,z),add(y,x,z);
        Sz(1,0);work(1,1);puts("16");
        for (int i=1;i<=n;i++)
            for (int j=1;j<=16;j++)
                printf("%d",f[i][j]),
                putchar(j<16?' ':'
    ');
        return 0;
    }
  • 相关阅读:
    基于redis实现滑动窗口式的短信发送接口限流
    Linux 宝塔下的PHP如何与本地的nginx关联
    Linux 下php安装gd库
    Linux Mysql8重置密码
    PHP 无限分级类
    redis 缓存穿透,缓存雪崩,缓存击穿
    yii2 事务添加
    ConcurrentHashMap
    Volatile
    this引用的逸出
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12047376.html
Copyright © 2011-2022 走看看