#include<bits/stdc++.h>#defineLLlonglong
using namespace std;constint N =2e3+10, INF =0x3f3f3f3f, MOD =10007;int n;
LL e[N][N], w[N];
LL maxx, sum;intmain(){scanf("%d",&n);memset(e,0x3f,sizeof(e));for(int i =1; i < n; i ++){int u, v;scanf("%d%d",&u,&v);
e[u][v]= e[v][u]=1;}for(int i =1; i <= n; i ++)scanf("%lld",&w[i]);for(int k =1; k <= n; k ++){for(int i =1; i <= n; i ++){for(int j =1; j <= n; j ++){if(i != j && j != k && k != i){
e[i][j]=min(e[i][j], e[i][k]+ e[k][j]);}}}}for(int i =1; i <= n; i ++){for(int j =1; j <= n; j ++){if(e[i][j]==2){
LL x = w[i]* w[j];
maxx =max(maxx, x);
sum =(sum + x)% MOD;}}}printf("%lld %lld", maxx, sum);return0;}
时间复杂度O(n),100分
#include<bits/stdc++.h>#defineLLlonglong
using namespace std;constint N =2e5+10, INF =0x3f3f3f3f, MOD =10007;int n, u[N], v[N];
LL w[N], sumA[N], sumB[N], m1[N], m2[N];//权值,和,平方和,最大,次大
LL maxx, sum;voidlink(int x,int y){
sumA[x]=(sumA[x]+ w[y])% MOD;
sumB[x]=(sumB[x]+ w[y]* w[y])% MOD;if(w[y]> m1[x]) m2[x]= m1[x], m1[x]= w[y];elseif(w[y]> m2[x]) m2[x]= w[y];}intmain(){scanf("%d",&n);for(int i =1; i < n; i ++)scanf("%d%d",&u[i],&v[i]);for(int i =1; i <= n; i ++)scanf("%lld",&w[i]);for(int i =1; i <= n; i ++)link(u[i], v[i]),link(v[i], u[i]);for(int i =1; i <= n; i ++){
LL t = sumA[i]* sumA[i]- sumB[i];
sum =(sum + t)% MOD;
maxx =max(maxx, m1[i]* m2[i]);}printf("%lld %lld\n", maxx, sum);return0;}
用树来实现,遍历n个点,时间复杂度O(n),100分
#include<bits/stdc++.h>#defineLLlonglong
using namespace std;constint N =2e5+10, M =2* N, MOD =10007;int n;int h[N], to[M], nxt[M], idx;
LL maxx, sum, w[N];voidadd(int u,int v){ to[++ idx]= v; nxt[idx]= h[u]; h[u]= idx;}voiddfs(int r,int f,int g){
LL sumA =0, sumB =0, m1 =0, m2 =0;//和,平方和,最大,次大 for(int i = h[r]; i; i = nxt[i]){int v = to[i];if(v == f)continue;
sumA =(sumA + w[v])% MOD;
sumB =(sumB + w[v]* w[v])% MOD;if(w[v]> m1) m2 = m1, m1 = w[v];elseif(w[v]> m2) m2 = w[v];dfs(v, r, f);}
LL tmp =max(m1 * m2, w[r]* w[g]);
maxx =max(maxx, tmp);
tmp =(sumA * sumA - sumB +2* w[r]* w[g])% MOD;
sum =(sum + tmp)% MOD;}intmain(){scanf("%d",&n);for(int i =1; i < n; i ++){int u, v;scanf("%d%d",&u,&v);add(u, v);add(v, u);}for(int i =1; i <= n; i ++)scanf("%lld",&w[i]);dfs(1,0,0);printf("%lld %lld\n", maxx, sum);return0;}
2018年 提高组D1T3 赛道修建
40分:退化成链,或者只修建一条赛道
#include<bits/stdc++.h>#defineLLlonglong
using namespace std;constint N =5e4+10, M =2* N;int n, m;int h[N], to[M], wt[M], nxt[M], idx;structedge{int a, b, l;}e[N];voidadd(int u,int v,int w){ to[++ idx]= v; wt[idx]= w; nxt[idx]= h[u]; h[u]= idx;}///
bool cmp1(edge x, edge y){return x.a < y.a;}voidf1(){sort(e+1, e+n, cmp1);int l =0, r =5e8, ans =0;while(l <= r){int mid = l + r >>1;int cnt =0, sum =0;for(int i =1; i < n; i ++){
sum += e[i].l;if(sum >= mid){
cnt ++;
sum =0;}}if(cnt >= m) ans = mid, l = mid +1;else r = mid -1;}printf("%d\n", ans);}///int q[N], hh, tt, len[N];voidbfs(int x){memset(len,-1,sizeof(len));
hh = tt =0;
q[++ tt]= x;
len[x]=0;while(hh < tt){int u = q[hh +1];
hh ++;for(int i = h[u]; i; i = nxt[i]){int v = to[i];if(len[v]!=-1)continue;
q[++ tt]= v;
len[v]= len[u]+ wt[i];}}}voidf2(){int maxx;//以任意点为根,距离最远的点就是直径上的端点之一 bfs(1);
maxx =0;for(int i =1; i <= n; i ++){if(len[i]> len[maxx]){
maxx = i;}}//以找到的端点为根,再找一次,距离最远的点就是直径上的另一个端点 bfs(maxx);
maxx =0;for(int i =1; i <= n; i ++){if(len[i]> len[maxx]) maxx = i;}printf("%d\n", len[maxx]);}///intmain(){scanf("%d%d",&n,&m);
bool chain = true;for(int i =1; i < n; i ++){int a, b, l;scanf("%d%d%d",&a,&b,&l);
e[i].a = a; e[i].b = b; e[i].l = l;//存边 add(a, b, l);add(b, a, l);//邻接矩阵 if(b != a +1) chain = false;}if(chain)f1();//退化成链 elseif(m ==1)f2();//只修建一条赛道 return0;}
正解100分
#include<bits/stdc++.h>
using namespace std;constint N =5e4+10, M =2* N;int n, m, cnt;int l, r, mid;int h[N], to[M], wt[M], nxt[M], idx;int f[N];voidadd(int u,int v,int w){ to[++ idx]= v; wt[idx]= w; nxt[idx]= h[u]; h[u]= idx;}//子树的根节点,父节点voiddfs(int u,int fa){
multiset <int> s;
multiset <int>:: iterator it, it1;for(int i = h[u]; i; i = nxt[i]){int v = to[i], w = wt[i];if(v == fa)continue;dfs(v, u);//遍历根节点为v的子树,此时u是v的父节点if(f[v]+ w >= mid) cnt ++;//赛道数量 + 1 else s.insert(f[v]+ w);//将长度不够的链保留下来 }while(!s.empty()){
it = s.begin();//最短的一条链
s.erase(it);
it1 = s.lower_bound(mid -*it);//试图找一条能和最短链组合成赛道且赛道最短的链 if(it1 == s.end()){
f[u]=max(f[u],*it);//如果找不到,将这条链的长度作为f[u]的候选项 }else{
cnt ++;//如果能找到,赛道数量 + 1
s.erase(it1);}}}
bool check(){memset(f,0,sizeof(f));
cnt =0;dfs(1,0);//子树的根节点,父节点return cnt >= m;}intmain(){scanf("%d%d",&n,&m);for(int i =1; i < n; i ++){int u, v, w;scanf("%d%d%d",&u,&v,&w);add(u, v, w);add(v, u, w);
r += w;}
l =0; r = r / m;//长度最小的赛道的长度不超过总长度 / m int ans =0;while(l <= r){
mid = l + r >>1;if(check()) ans = mid, l = mid +1;else r = mid -1;}printf("%d\n", ans);return0;}