0x10 题目链接
0x20 题目
0x21 Tag
图论,数学
0x22 题目描述
0x30 思路与算法
当然最大联合权值比较好求, 只需要求出当前节点cur
四周点的最大值和次大值。
对于联合权值之和, 假设当前节点cur
所连接的点的权值为
v
1
,
v
2
,
⋯
v_1,v_2,⋯
v1,v2,⋯那么节点cur
对权值之和产生的贡献为:
W
=
∑
v
i
∑
v
j
v
i
v
j
W=\sum_{v_i}\sum_{v_j}v_iv_j
W=vi∑vj∑vivj
=
∑
v
i
v
i
∑
v
j
!
=
v
i
v
j
=\sum_{v_i}v_i\sum_{v_j!=v_i}v_j
=vi∑vivj!=vi∑vj
=
∑
v
i
v
i
[
(
∑
v
j
v
j
)
−
v
i
]
=\sum_{v_i}v_i[(\sum_{v_j}v_j)-v_i]
=vi∑vi[(vj∑vj)−vi]
=
(
∑
v
i
v
i
)
2
−
∑
v
i
v
i
2
=(\sum_{v_i}v_i)^2-\sum_{v_i}v_i^2
=(vi∑vi)2−vi∑vi2
将每个节点的W加和即得联合权值之和。
0x40 代码
0x41 实现细节
声明
const int MAXN=2e5+5;
int ans1=0,ans2=0;
int a[MAXN];
int n;
这里ans1记录联合权值最大值,ans2记录联合权值之和。n是节点数,a数组存储节点权值。
存图
struct Edge
{
int to, w, next;
}edges[MAXN<<1];
int heads[MAXN], cnt; // cnt为当前边的编号
inline void add(int from, int to, int w = 1)
{
edges[++cnt].w = w; //新增一条编号为cnt+1的边,边权为w
edges[cnt].to = to; //该边的终点为to
edges[cnt].next = heads[from]; //把下一条边,设置为当前起点的第一条边
heads[from] = cnt; //该边成为当前起点新的第一条边
}
由于不知道根节点,存一个无向图方便dfs
DFS
void dfs(int cur,int fath)
{
if(cur>n) return;
int m1=0,m2=0;
int sq=0,sum=0;
for (int eg = heads[cur]; eg != 0; eg = edges[eg].next)
{
int to=edges[eg].to;
if(to!=fath) dfs(to,cur);
if(a[to]>m2)
{
if(a[to]>m1)
{
m2=m1;
m1=a[to];
}
else m2=a[to];
}
sq+=((a[to]%MOD)*(a[to]%MOD))%MOD;
sq%=MOD;
sum+=a[to]%MOD;
sum%=MOD;
}
ans1=max(ans1,m1*m2);
ans2+=((((sum%MOD)*(sum%MOD))%MOD-sq%MOD)+MOD)%MOD;
ans2%=MOD;
}
其中的if(cur>n) return;
与if(to!=fath) dfs(to,cur);
防止反复DFS。两个if判断最小值和次小值。注意这里的MOD,做减法要在最后加MOD,如果开了long long
就不用每一项都模了。
0x42 完整代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 10007;
const ll INF = 0x3f3f3f3f;
// <------------------------------->
#define endl "\n"
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define HACK freopen("test.in", "r", stdin);freopen("test.out", "w", stdout);
#define RT double rtime = 1.0 * clock() / CLOCKS_PER_SEC;cout<<"\nRuntime: "<<rtime<< " s.\n";
#define debug(x) cout<<"target is "<<x<<endl
#define debug_arr(arr) for(auto x:arr) cout<<x<<" "; cout<<"\n";
// <------------------------------->
const int MAXN=2e5+5;
int ans1=0,ans2=0;
int a[MAXN];
int n;
struct Edge
{
int to, w, next;
}edges[MAXN<<1];
int heads[MAXN], cnt; // cnt为当前边的编号
inline void add(int from, int to, int w = 1)
{
edges[++cnt].w = w; //新增一条编号为cnt+1的边,边权为w
edges[cnt].to = to; //该边的终点为to
edges[cnt].next = heads[from]; //把下一条边,设置为当前起点的第一条边
heads[from] = cnt; //该边成为当前起点新的第一条边
}
//<------------------------------->
void dfs(int cur,int fath)
{
if(cur>n) return;
int m1=0,m2=0;
int sq=0,sum=0;
for (int eg = heads[cur]; eg != 0; eg = edges[eg].next)
{
int to=edges[eg].to;
if(to!=fath) dfs(to,cur);
if(a[to]>m2)
{
if(a[to]>m1)
{
m2=m1;
m1=a[to];
}
else m2=a[to];
}
sq+=((a[to]%MOD)*(a[to]%MOD))%MOD;
sq%=MOD;
sum+=a[to]%MOD;
sum%=MOD;
}
ans1=max(ans1,m1*m2);
ans2+=((((sum%MOD)*(sum%MOD))%MOD-sq%MOD)+MOD)%MOD;
ans2%=MOD;
}
int main()
{
IOS;
#ifdef LOCAL_JUDGE
HACK;
#endif
cin>>n;
for(int i=1;i<=n-1;++i)
{
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1;i<=n;++i)
cin>>a[i];
dfs(1,0);
cout<<ans1<<" "<<ans2;
#ifdef LOCAL_JUDGE
RT;
#endif
return 0;
}
0x50 另
代码仅代表个人答案。如有错误,请多指正。=)