Noip2014模拟赛解题心得【联合权值】

首先说明一下,我是一个刚刚进入oi界不到半年的蒟蒻——半年来,我是仅仅凭着兴趣和决心,在校队的一群dalao中瑟瑟发抖。我做的很多题目都是“苟”出来的,就比如说这次模拟赛中的“联合权值”,仅凭暴力只坑了40分。

现在就从这道题目开始分析吧。题目如下:

标题: 联合权值

详情:

输入格式:

第一行包含 1 个整数 n。
接下来 n-1 行,每行包含 2 个用空格隔开的正整数 u、v,表示编号为 u 和编号为 v 的点
之间有边相连。
最后 1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示
图 G 上编号为 i 的点的权值为 Wi。

输出格式:
输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值
和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对 10007 取余。

限制: 对于 30%的数据,1 < ?? ≤ 100;对于 60%的数据,1 < ?? ≤ 2000;对于 100%的数据,1 < ?? ≤ 200,000,0 < ??! ≤ 10,000。

样例:

输入

5
1 2
2 3
3 4
4 5
1 5 2 3 1 0

输出

20 74

解释

本例输入的图如上所示,距离为 2 的有序点对有(1,3)、(2,4)、(3,1)、(3,5)、(4,2)、(5,3)。 其联合权值分别为 2、15、2、20、15、20。其中最大的是 20,总和为 74。

-----------------华丽的分界线---------------------

我自己做的(40分的)(超时60%)代码如下:

 1 #include <cstdio>
 2 #define N 200001
 3 struct enode {
 4     int to;
 5     enode *next;
 6 };
 7
 8 struct vnode {
 9     int form;
10     enode *first;
11 } v[N];
12
13 int main() {
14     int k,i,j,w,n,xx[N],maxx=0,sum=0,tmp;
15     scanf("%d",&n);
16     for (k=1;k<=n-1;k++) {
17         scanf("%d%d",&i,&j);
18         enode *p=new enode();
19         p->to=j;
20         p->next=v[i].first;
21         v[i].first=p;
22         enode *q=new enode();
23         q->to=i;
24         q->next=v[j].first;
25         v[j].first=q;
26     }
27     for (i=1;i<=n;i++) scanf("%d",&xx[i]);
28     for (i=1;i<=n;i++) {
29         for (enode *qq=v[i].first;qq!=NULL;qq=qq->next) {
30             for (enode *pp=v[qq->to].first;pp!=NULL;pp=pp->next) {
31                 if (i!=pp->to) {
32                     //printf("%d %d\n",i,pp->to);
33                     tmp=xx[i]*xx[pp->to];
34                     if (maxx<tmp) maxx=tmp;
35                     sum+=tmp;
36                 }
37             }
38         }
39     }
40     printf("%d %d",maxx,sum);
41     return 0;
42 }

可见,该代码不仅硬套模板,而且花了大半的时间重复搜索,这也就是它只得了20分的原因。

那么,正确的解法如何呢?

因为题目中的图是一棵树,所以可以用广搜来搜索每个节点的父节点和子节点,将其两两匹配,再将所有的子节点两两匹配。以数据

5
1 2
2 3
3 4
4 5
1 5 2 3 1 0

为例

gw.x,表示节点编号 1 2 3 4 5
gw.f,表示父节点 0 1 2 3 4

顺序是,1-3,、2-4、3-5

然后将值乘起来,算得结果(60分无误)

不过这道题的优化可以使用dp,思路如下 (怎么竟然是dp我的天啊)

伪代码:

先存节点:

1 -2 -3

2 -1 -4 -5

3 -1 -6 -7

4 -2

5 -2

6 -3

7 -3

边:2-3, 1-4, 1-5, 4-5, 1-6, 1-7, 2-7

两两比较,在n个顶点找最大值与次大值,时间复杂度O(n^2)

——但是,若第一顶点就有n个顶点,那么这个时间复杂度就不成立;事实上,时间复杂度不可能为n^2,而是没有那么复杂。

综上,这tm就是一道彻彻底底的数学题!根本就和图论没有半毛钱关系!!!

标程如下:

 1 #include<map>
 2 #include<set>
 3 #include<cmath>
 4 #include<stack>
 5 #include<queue>
 6 #include<cstdio>
 7 #include<vector>
 8 #include<cstring>
 9 #include<cstdlib>
10 #include<iostream>
11 #include<algorithm>
12 #define mod 10007
13 #define pi acos(-1)
14 #define inf 0x7fffffff
15 #define ll long long
16 using namespace std;
17 ll read()
18 {
19     ll x=0,f=1;char ch=getchar();
20     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
21     while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
22     return x*f;
23 }
24 int n;
25 ll w[200005],mx[200005],sum[200005];
26 ll ans1[200005],ans2[200005];
27 vector<int>e[200005];
28 void dp(int x,int fa)
29 {
30     for(int i=0;i<e[x].size();i++)
31     {
32         int y=e[x][i];
33         if(y==fa)continue;
34         dp(y,x);
35         mx[x]=max(mx[x],w[y]);
36         sum[x]=(sum[x]+w[y])%mod;
37         ans1[x]=max(ans1[x],w[x]*mx[y]);
38         ans1[x]=max(ans1[x],ans1[y]);
39         ans2[x]=(ans2[x]+w[x]*sum[y])%mod;
40         ans2[x]=(ans2[x]+ans2[y])%mod;
41     }
42     ll t1=0,t2=0;
43     for(int i=0;i<e[x].size();i++)
44     {
45         int y=e[x][i];
46         if(y==fa)continue;
47         ans1[x]=max(ans1[x],t1*w[y]);
48         ans2[x]=(ans2[x]+t2*w[y])%mod;
49         t1=max(t1,w[y]);
50         t2=(t2+w[y])%mod;
51     }
52 }
53 int main()
54 {
55     n=read();
56     for(int i=1;i<n;i++)
57     {
58         int u=read(),v=read();
59         e[u].push_back(v);
60         e[v].push_back(u);
61     }
62     for(int i=1;i<=n;i++)
63     {
64         w[i]=read();
65         w[i]%=mod;
66     }
67     dp(1,0);
68     cout<<ans1[1]<<‘ ‘<<ans2[1]*2%mod<<endl;;
69     return 0;
70 }

这道题由老师给出的标程,更加简洁:

 1 #include<cstdio>
 2 #include<vector>
 3 #define modd 10007
 4 using namespace std;
 5
 6 int w[200006],n,x,y,max1,k1,k2,l,tot,s[200006],sum;
 7 vector<int> a[200006];
 8
 9 int main()
10 {
11     scanf("%d",&n);
12     for(int i=1;i<n;i++)
13     {
14         scanf("%d%d",&x,&y);
15         a[x].push_back(y);
16         a[y].push_back(x);
17     }
18     for(int i=1;i<=n;i++) scanf("%d",&w[i]);
19     for(int j=1;j<=n;j++)
20       if(a[j].size()>1)
21       {
22           k1=k2=0;
23           for(int i=0;i<a[j].size();i++)
24           {
25               if(w[a[j][i]]>k1)
26                 {
27                     k2=k1;k1=w[a[j][i]];  //k2=k1一定要写
28                 }
29               else if(w[a[j][i]]>k2) k2=w[a[j][i]];
30                 s[j]+=w[a[j][i]];s[j]%=modd;
31           }
32           l=k1*k2;
33           if(l>max1)
34           {
35               max1=l;continue;
36           }
37       }
38     for(int i=1;i<=n;i++)
39         if(a[i].size()>1)
40             for(int j=0;j<a[i].size();j++)
41             {
42                 sum+=w[a[i][j]]*(s[i]-w[a[i][j]]);
43                 sum%=modd;
44             }
45     printf("%d %d\n",max1,sum);
46     return 0;
47 }  

网上某dalao的回答:动规题无误。思路:

考虑树形dp:

一个结点距离相差2的点要不然是儿子的儿子,不然是兄弟

先考虑第一部分

只要记录一个结点儿子的权值和sum[x],以及权值的最大值mx[x]

ans1x=max{mxyi?wx}

ans2x=∑sumyi?wx

总之,dp什么的都去死吧。

最怕dp ( Difficult Problem )

时间: 2024-08-03 09:08:20

Noip2014模拟赛解题心得【联合权值】的相关文章

NOIP2014提高组第二题联合权值

还是先看题吧: 试题描述  无向连通图 G 有 n 个点,n-1 条边.点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi ,每条边的长度均为 1.图上两点(u, v)的距离定义为 u 点到 v 点的最短距离.对于图 G 上的点对(u, v),若它们的距离为 2,则它们之间会产生Wu * Wv 的联合权值.请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入 第一行包含 1 个整数 n.接下来 n-1 行,每行包含 2 个用空格隔开的正整数 

【前缀和】【前缀MAX】洛谷 P1351 NOIP2014提高组 day1 T2 联合权值

不难发现,树中与某个点距离为2的点只可能是它的父亲的父亲.儿子的儿子 或者 兄弟,分类讨论一下即可. 只有对于兄弟我们不能暴力搞,维护一下每个节点的所有儿子的前缀和.前缀MAX就行了. 1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 using namespace std; 5 #define N 200001 6 #define MOD 10007 7 int n; 8 vector<i

NOIP2014联合权值——史上最强解析

[解题思路] (虽然说下面的话可能有点多,但鄙人还是希望各位能看完,对理解大有裨益) 总的来说,本体让求两个值,一个所有权值联合后的和,二是最大的联合权值,那好,我们暂且先来看看这最大的联合权值 所谓联合权值,就是两个距离为2的节点的全职的乘积,那怎么乘着最大呢?对一个点来说,最大的乘积自然就是和它相邻的两个权值最大的节点的权值的乘积, 至于求这两个最大值,我们完全可以在读入数据的时候顺便处理一下嘛,举手之劳而已!! 对于所有的联合权值之和,我们要求你还是要有一点数学基础的,但也不高,三年级就足

[NOIP2014]联合权值

描述 无向连通图G有n个点,n-1条边.点从1到n依次编号,编号为i的点的权值为Wi  ,每条边的长度均为1.图上两点(u, v)的距离定义为u点到v点的最短距离.对于图G上的点对(u, v),若它们的距离为2,则它们之间会产生Wu×Wv的联合权值. 请问图G上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入格式 输入文件名为link.in. 第一行包含1个整数n. 接下来n-1行,每行包含2个用空格隔开的正整数u.v,表示编号为u和编号为v的点之间有边相连.

【NOIP之旅】NOIP2014 day1 T2 联合权值

2.联合权值 (link.cpp/c/pas) [问题描述] 无向连通图G有n个点,n-1条边.点从1到n依次编号,编号为i的点的权值为Wi  ,每条边的长度均为1.图上两点(u, v)的距离定义为u点到v点的最短距离.对于图G上的点对(u, v),若它们的距离为2,则它们之间会产生Wu×Wv的联合权值. 请问图G上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? [输入] 输入文件名为link.in. 第一行包含1个整数n. 接下来n-1行,每行包含2个用空格隔开

[NOIP2014] 提高组 洛谷P1351 联合权值

题目描述 无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 .图上两点( u , v ) 的距离定义为u 点到v 点的最短距离.对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu ×Wv 的联合权值. 请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入输出格式 输入格式: 输入文件名为link .in. 第一行包含1 个整数n . 接下来n - 1 行,

【学术篇】luogu1351 [NOIP2014提高组] 联合权值

一道提高组的题..... 传送门:题目在这里.... 现在都懒得更自己的blog了,怕是太颓废了_ (:з」∠) _ 好久没做题了,手都生了.(好吧其实是做题方面手太生了) 这题我都不想讲了,把代码一贴就算了呗.. 但还是要说说的.... 首先,题目里说:"无向连通图G 有n 个点,n - 1 条边." 我们可以知道这是一棵树(怕不是废话..),这样遍历的时候就能保证是O(n)级别了.. 找最大值 很简单,遍历树的时候找一下与每个点相连的点的最大值和次大值一乘就完了...显然这么贪心是

$Noip2014/Luogu1351$ 联合权值 树形

$Luogu$ $Description$ 给定一棵树,每两个距离为$2$的点之间可以产生"联合权值","联合权值"定义为这两个数的乘积.求最大的联合权值以及所有的联合权值之和.注意这两个数是有序的,翻译成人话就是求完和之后要$*2$. $Sol$ 想起了消防局的设立$ovo$. 距离为$2$的点,它们不是兄弟就是祖孙,那直接$dfs$一遍更新答案就好了叭. 兄弟之间更新答案这里有两个优化: 1.贪心.把所有的兄弟加入数组$s$之后按照$w[i]$从大到小排序,一遍

20161027模拟赛解题报告

20161027模拟赛解题报告 By shenben T1 数学题 模拟即可. 注意开long long T2 技巧题 图片为本题第一张图.(无奈,图传不上来) 首先第一问图中的“Y 字形”的数量,这么简单,在此不细讲. 详见代码 O(n)累加一下就好了 主要说说第二问怎么搞 预处理 每个点分别与其他那些点相连 权值为第1,2,3大(若没有2,3大,就忽略).记录一下权值与对应的点的标号.目的是方便下面的判断. 枚举入度>=3的点,即点B(有多个) 再枚举点B相连的D点(不是点A,C). Ste