zoukankan      html  css  js  c++  java
  • [NOIP2014-提高组] 联合权值

    • 前言

      未经允许,请勿转载。


    • 题目

      无向连通图 (G)(n) 个点,(n-1) 条边。点从 (1)(n) 依次编号,编号为 (i) 的点的权值为 (W_i) ,每条边的长度均为 (1)。图上两点 ((u,v)) 的距离定义为 (u) 点到 (v) 点的最短距离。对于图 (G) 上的点对 ((u,v)) ,若它们的距离为 (2) ,则它们之间会产生 (W_v imes W_u) 的联合权值。

      请问图 (G) 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?


    • 分析题目

      1. 无向连通图 (G)(n) 个点,(n-1) 条边。

        很明显,给出的是一个无根树

      2. 由于所有联合权值之和可能很大,输出它时要对(10007)取余。

        易错点,最大值不用取膜,和才要取膜。


    • 分析

      因为他是一个无根树,所以不如先给他确认(1)为其根节点

      因为他是一个树,那么对于每个点只要考虑(3)种情况:父节点的父节点(爷爷),他的子节点的子节点和他有同一个父亲的任意一点(孙子们)

      然后又可以发现,可以忽略每个点的子节点的子节点的计算 ,相应的,只要把每次算父节点的父节点时的值算上2遍就可以了。

      这里稍微解释一下:(理解的人自动跳过)

      (x) 的父节点的父节点是 (y)

      在算 (x) 的情况时,要以第一种情况(父节点的父节点) 求一次 (w_x*w_y)。算 (y) 的情况时,也要以第二种情况(子节点的子节点) 算一遍 (w_y * w_x)

      所以可以直接忽略第二种情况,把第一种情况的结果乘上 (2)

      好了继续。

      这么一分析完,就可以直接跑 (dfs) 了。

      对于每个点,第一种情况直接算(直接乘法);第二种情况,直接枚举和自己有同一个父亲的所有点,不断更新答案即可。


    • 一点细小的优化(我当时以为不加能过)

      直接(dfs)直接TLE,还需要一点点优化。

      看刚刚最后的结论,发现每次枚举的点权是可以预处理的。

      (sum[x]) 表示(x) 所有子节点点权和

      而最大值,对于一个点 (x) ,也求出他的子节点的最大值和次大值。(因为下一层的最大值的点不能和自己乘)

      然后就可以了。

      如果还有些不理解,可以看看代码。


    • 代码
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int dalao=10007;
    struct edge{
    	int v,nx;
    }p[400005];
    int n,ne,f[200005];
    int sum[200005],father[200005],w[200005];
    int data[200005][2],gmax[200005][2];//data记录编号 gmax记录具体值 0最大值 1次大值 
    int maxn,ans;
    void read(int u,int v)
    {	p[++ne].v=v;
    	p[ne].nx=f[u];
    	f[u]=ne;
    }
    void getfather(int x,int fa)
    {	father[x]=fa;
    	for(int i=f[x];i;i=p[i].nx)
    	{	if(p[i].v==fa)continue;
    		getfather(p[i].v,x);
    	}
    }
    int main()
    {	scanf("%d",&n);
    	for(int i=1;i<n;i++)
    	{	int u,v;
    		scanf("%d%d",&u,&v);
    		read(u,v);
    		read(v,u);
    	}
    	for(int i=1;i<=n;i++)
    		scanf("%d",&w[i]);
    	sum[0]=w[1];
    	getfather(1,0);//确认每个点的父节点 
    	for(int i=1;i<=n;i++)//预处理 
    		for(int j=f[i];j;j=p[j].nx)
    		{	if(p[j].v==father[i])continue;
    			sum[i]=(sum[i]+w[p[j].v])%dalao;//预处理和
    			//预处理最大值和次大值 
    			if(w[p[j].v]>gmax[i][0]){gmax[i][0]=w[p[j].v];data[i][0]=p[j].v;}
    			else if(w[p[j].v]>gmax[i][1]){gmax[i][1]=w[p[j].v];data[i][1]=p[j].v;}
    		}
    	for(int x=1;x<=n;x++)
    	{	if(father[father[x]])//存在父节点的父节点 
    			ans=(ans+2*w[x]*w[father[father[x]]])%dalao;
    		if(father[father[x]])
    			maxn=max(maxn,w[x]*w[father[father[x]]]);
    		ans=(ans+w[x]*(sum[father[x]]-w[x]))%dalao;
    		
    		if(data[father[x]][0]!=x)maxn=max(maxn,w[x]*gmax[father[x]][0]);
    		else maxn=max(maxn,w[x]*gmax[father[x]][1]);
    	}
    	printf("%d %d
    ",maxn,ans);
    	return 0;
    }
    

    [ ext{by Rainy7} ]

  • 相关阅读:
    Wpf 简单制作自己的窗体样式(2)
    Wpf 简单制作自己的窗体样式
    Microsoft Expression Blend 4制作简单的按钮
    Jest和enzyme 前端单元测试工具
    使用socket.io实现多房间通信聊天室
    如何快速开发一个微信小游戏--实例《打气球》
    使用flow来规范javascript的变量类型
    前端开发工具icestar
    vue.js和vue-router和vuex快速上手知识
    react.js插件开发,x-dailog弹窗浮层组件
  • 原文地址:https://www.cnblogs.com/Rainy7/p/12275983.html
Copyright © 2011-2022 走看看