• 【2019杭州集训12.09】向量


    Description

    • 给定一棵 nn 个节点的树,点的标号为 1..n1..n,边有边权。
    • d(u,v)d(u, v)uuvv 的路径上边的权值和,对于每个节点 uu,你需要给出一个 mm
      向量 pu=pu ={ pu,1,..,pu,mp_{u,1}, .., p_{u,m} }。
      使得对于任意点对 u,vu,v,满足 d(u,v)=maxpu,ipv,id(u, v) = max{|p_{u,i} − p_{v,i}|}
      在这里插入图片描述
    • n<=1000n<=1000

    Solution

    • 刚开始看错题意了QwQ,没有注意到向量的位置是对应的。
    • 暴力的构造一个答案的方法就是直接以每一个点为根多一维,每一个点在这一维的值即为它的深度。
    • 我们可以通过调整根的选择和正负号来使得维数最少。
    • 点分治。点分治的每一层就是每一维,最后的维数就是层数。
    • 每一次将儿子分成尽量均匀的两块,一边是 dep-dep,一边是depdep,这样子这两边互相就可以满足了,这样就只用考虑子问题了。
    • 不同的两块可能在这一维有共点。直接给边赋权值,从一个点开始统一就好了。
    • 注意到会不会子问题的答案之间互相影响会不会超过两两的距离。实际上是每条边的*+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
    ",maxdep);
    	for(j=1;j<=n;j++,printf("
    ")) for(i=1;i<=maxdep;i++) printf("%d ",ans[i][j]);
    }
    
    
  • 相关阅读:
    我在面试中碰到的面试题
    JavaScript中数组去重的几种方法整理
    html网页外框布局设计总结
    css+Jquery实现抽拉式导航条和页面内容适应
    jquery不能实现对dom元素的伪类元素样式进行操作
    css的文字颜色渐变
    javascript函数立即调用
    javascript闭包
    js异步原理
    关于浏览器兼容问题
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090902.html
Copyright © 2020-2023  润新知