这题自己(真正)思考了很久(欣慰)。
(轻而易举)地发现这是一棵树后,打算从Dfs序中下功夫,推敲了很久规律,没看出来(太弱了)。
开始手动枚举距离为2的情况,模模糊糊有了一些概念,但没有总结。(敲黑板:题目中发现规律与重要性质注意总结!)
其实,距离为2的情况只有两种:祖父/兄弟。
一个小时后放弃治疗。开始想暴力,很好想,我们对于每个点,枚举他的出边,再在每个出边中的出边中进行枚举,储存距离为2 的点。期望得分60pts.
大力交了一下:40pts,AC*6,WA*2,MLE*4.
MLE还有情可缘,vector开动态数组可能炸了,WA的那两个喵喵喵?
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 6 using namespace std; 7 typedef long long ll; 8 9 int n,tot; 10 ll sum,ans,p=10007; 11 int head[200090]; 12 ll val[200090]; 13 bool vis[200090]; 14 struct node{ 15 int to,next; 16 }edge[400090]; 17 vector<int>law[200090]; 18 19 void add(int x,int y) 20 { 21 edge[++tot].to=y; 22 edge[tot].next=head[x]; 23 head[x]=tot; 24 } 25 26 27 void dfs(int u) 28 { 29 for(int i=head[u];i;i=edge[i].next) 30 { 31 int v=edge[i].to; 32 for(int j=head[v];j;j=edge[j].next) 33 { 34 int g=edge[j].to; 35 if(g==u) continue; 36 law[u].push_back(g); 37 } 38 } 39 } 40 41 int main() 42 { 43 scanf("%d",&n); 44 for(int i=1;i<=n-1;i++) 45 { 46 int x=0,y=0; 47 scanf("%d%d",&x,&y); 48 add(x,y); 49 add(y,x); 50 } 51 for(int i=1;i<=n;i++) scanf("%d",&val[i]); 52 // dfs_pre(1); 53 // memset(vis,0,sizeof(vis)); 54 // dfs(1,0); 55 for(int i=1;i<=n;i++) 56 dfs(i); 57 for(int i=1;i<=n;i++) 58 { 59 for(int j=0;j<law[i].size();j++) 60 { 61 int u=i,v=law[i][j]; 62 ll tmp=val[u]%p*val[v]%p; 63 (sum+=tmp)%=p; 64 (ans=max(ans,tmp))%=p; 65 } 66 } 67 printf("%lld %lld",ans,sum); 68 return 0; 69 }
后来经过冷静分析看题解发现并不需要存儿子,当时每次更新一下就行了。而且最大值并不需要取膜。
再交一下60pts。TLE4个点,正常。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 6 using namespace std; 7 typedef long long ll; 8 9 int n,tot; 10 ll sum,ans,p=10007; 11 int head[200090]; 12 ll val[200090]; 13 struct node{ 14 int to,next; 15 }edge[400090]; 16 17 void add(int x,int y) 18 { 19 edge[++tot].to=y; 20 edge[tot].next=head[x]; 21 head[x]=tot; 22 } 23 24 25 void dfs(int u) 26 { 27 for(int i=head[u];i;i=edge[i].next) 28 { 29 int v=edge[i].to; 30 for(int j=head[v];j;j=edge[j].next) 31 { 32 int g=edge[j].to; 33 if(g==u) continue; 34 ans=max(ans,val[u]*val[g]); 35 (sum+=val[u]*val[g])%=p; 36 } 37 } 38 } 39 40 int main() 41 { 42 scanf("%d",&n); 43 for(int i=1;i<=n-1;i++) 44 { 45 int x=0,y=0; 46 scanf("%d%d",&x,&y); 47 add(x,y); 48 add(y,x); 49 } 50 for(int i=1;i<=n;i++) scanf("%d",&val[i]); 51 // dfs_pre(1); 52 // memset(vis,0,sizeof(vis)); 53 // dfs(1,0); 54 for(int i=1;i<=n;i++) 55 dfs(i); 56 printf("%lld %lld",ans,sum); 57 return 0; 58 }
正解:我们只需要枚举每个点与他相连的每一条边即可,统计出与每个点相邻的最大点值与次大点值,全局最值用(最大点值*次大点值)更新,全局和用“乘法分配律“”维护。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 7 int n,tot; 8 ll p=10007,sum,ans; 9 int head[200090],val[200090]; 10 struct node{ 11 int to,next; 12 }edge[400090]; 13 14 void add(int x,int y) 15 { 16 edge[++tot].to=y; 17 edge[tot].next=head[x]; 18 head[x]=tot; 19 } 20 21 ll llmax(ll a,ll b) 22 { 23 if(a>=b) return a; 24 else return b; 25 } 26 27 void update(int x) 28 { 29 int maxx=0,maxs=0; 30 ll cnt=0; 31 for(int i=head[x];i;i=edge[i].next) 32 { 33 int y=edge[i].to; 34 if(val[y]>maxx) maxs=maxx,maxx=val[y]; 35 else if(val[y]>maxs) maxs=val[y]; 36 (sum+=cnt*val[y])%=p; 37 (cnt+=val[y])%=p; 38 } 39 ans=llmax(ans,maxs*maxx); 40 } 41 42 int main() 43 { 44 scanf("%d",&n); 45 for(int i=1;i<=n-1;i++) 46 { 47 int x=0,y=0; 48 scanf("%d%d",&x,&y); 49 add(x,y);add(y,x); 50 } 51 for(int i=1;i<=n;i++) scanf("%d",&val[i]); 52 for(int i=1;i<=n;i++) update(i); 53 printf("%lld %lld",ans,2*sum%p); 54 return 0; 55 }