Description
- 给定一棵 n 个节点的树,点的标号为 1..n,边有边权。
- 记 d(u,v) 为 u 到 v 的路径上边的权值和,对于每个节点 u,你需要给出一个 m 维
向量 pu={ pu,1,..,pu,m }。
使得对于任意点对 u,v,满足 d(u,v)=max∣pu,i−pv,i∣。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191210093014984.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjQ5NDE2,size_16,color_FFFFFF,t_70)
- n<=1000
Solution
- 刚开始看错题意了QwQ,没有注意到向量的位置是对应的。
- 暴力的构造一个答案的方法就是直接以每一个点为根多一维,每一个点在这一维的值即为它的深度。
- 我们可以通过调整根的选择和正负号来使得维数最少。
- 点分治。点分治的每一层就是每一维,最后的维数就是层数。
- 每一次将儿子分成尽量均匀的两块,一边是 −dep,一边是dep,这样子这两边互相就可以满足了,这样就只用考虑子问题了。
- 不同的两块可能在这一维有共点。直接给边赋权值,从一个点开始统一就好了。
- 注意到会不会子问题的答案之间互相影响会不会超过两两的距离。实际上是每条边的*+1或-1加在一起,肯定不会超过全部正或全部负。
- 那么一共有多少层呢?
- 结论是每一次至少分n/3.
- 首先重儿子的大小<=n/2,如果最大重儿子sz>=n/3,得证。如果最大重儿子sz<n/3,那么每多一个儿子在n/3以内,最后平均的大小是不会超过2*n/3的,得证。
- 每一次大小至少乘2/3,对于1000正好有16层。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 1005
using namespace std;
int n,i,j,k,x,y,z;
int em,e[maxn*2],nx[maxn*2],ls[maxn],ec[maxn*2],h[20][maxn*2];
int bz[maxn],ans[20][maxn];
void insert(int x,int y,int z){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em; ec[em]=z;
em++; e[em]=x; nx[em]=ls[y]; ls[y]=em; ec[em]=z;
}
int g,sz[maxn],All;
void Getg(int x,int p){
int mx=0; sz[x]=1;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p&&!bz[e[i]])
Getg(e[i],x),sz[x]+=sz[e[i]],mx=max(mx,sz[e[i]]);
mx=max(mx,All-sz[x]);
if (mx<=All/2) g=x;
}
void Getsz(int x,int p){
sz[x]=1;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p&&!bz[e[i]])
Getsz(e[i],x),sz[x]+=sz[e[i]];
}
int tot;
struct node{int x,sz;} A[maxn];
int cmp(node a,node b){return a.sz>b.sz;}
int nowdep,maxdep;
void Cover(int x,int p,int k){
for(int i=ls[x];i;i=nx[i]) if (!bz[e[i]]){
if (e[i]==p) h[nowdep][i]=-k*ec[i],h[nowdep][i^1]=k*ec[i];
else Cover(e[i],x,k);
}
}
int d0[20][maxn],d1[20][maxn];
void Doit(int now,int all,int depth){
if (all==1) {bz[now]=1;return;}
int i; maxdep=max(maxdep,depth);
All=all,Getg(now,0);
Getsz(g,0);
for(tot=0,i=ls[g];i;i=nx[i]) if (!bz[e[i]])
tot++,A[tot].x=e[i],A[tot].sz=sz[e[i]];
sort(A+1,A+1+tot,cmp);
int sum0=0,sum1=0; nowdep=depth;
d0[depth][0]=d1[depth][0]=0;
for(i=1;i<=tot;i++) if (sum0<sum1)
sum0+=A[i].sz,d0[depth][++d0[depth][0]]=A[i].x,Cover(A[i].x,g,1);
else sum1+=A[i].sz,d1[depth][++d1[depth][0]]=A[i].x,Cover(A[i].x,g,-1);
int nowg=g;
if (all-sum0>2){
for(i=1;i<=d0[depth][0];i++) bz[d0[depth][i]]=1;
Doit(nowg,all-sum0,depth+1);
for(i=1;i<=d0[depth][0];i++) bz[d0[depth][i]]=0;
}
if (all-sum1>2){
for(i=1;i<=d1[depth][0];i++) bz[d1[depth][i]]=1;
Doit(nowg,all-sum1,depth+1);
for(i=1;i<=d1[depth][0];i++) bz[d1[depth][i]]=0;
}
}
void DFS(int x,int p,int D,int tp){
ans[tp][x]=D;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
DFS(e[i],x,D+h[tp][i],tp);
}
int main(){
freopen("ceshi.in","r",stdin);
freopen("ceshi.out","w",stdout);
scanf("%d",&n);
for(em=1,i=1;i<n;i++)
scanf("%d%d%d",&x,&y,&z),insert(x,y,z);
Doit(1,n,1);
for(i=1;i<=maxdep;i++) DFS(1,0,0,i);
printf("%d\n",maxdep);
for(j=1;j<=n;j++,printf("\n")) for(i=1;i<=maxdep;i++) printf("%d ",ans[i][j]);
}