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,表示节点编号12345
gw.f,表示父节点01234
  
 
 
顺序是,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{mxyiwx}
ans2x=sumyiwx

 

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

最怕dp ( Difficult Problem )

posted on 2017-09-23 16:57  Pyloj 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/wuyuema/p/noip2017Note-Noip2014_link_cpp.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值