C
题意:N个人按顺序(1~N)站成一圈,每一个人都有一个时间s表示他拿到球以后会在多久之后给下面一个人,另外给你一个序列T,ti表示takahashi会在ti绝对时间给第i个人一个球,问你每一个人第一次获得球的时间
方法:因为每个人的首次获得球的时间一定是由前面一个人第一次给他球的时间和takahashi给他球的时间决定的,所以先找第一个获得球的人,然后向后循环n - 1次,求出每个人的时间就行了
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
#define int long long
const int N = 200010, INF = 1e18;
struct node{
int s, t;
int ans;
}a[N];
int n;
signed main(){
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i].s;
for(int i = 1; i <= n; i ++){
cin >> a[i].t;
a[i].ans = a[i].t;
}
int k = 0, minv = INF;
for(int i = 1; i <= n; i ++){
if(minv > a[i].t){
minv = a[i].t;
k = i;
}
}
for(int i = 1; i < n; i ++){
int cur = k + i > n ? k + i - n : k + i;
int pre = cur - 1 ? cur - 1 : n;
a[cur].ans = min(a[cur].ans, a[pre].ans + a[pre].s);
}
for(int i = 1; i <= n; i ++) cout << a[i].ans << endl;
}
D
题意:给你一个树N个节点,N-1条边,求
[sum_{i = 1}^Nsum_{j = i + 1}^Nf(i, j)
]
其中f(i, j)是i到j的最短路径上的权值最大的边权
方法:将边按照边权升序排序,挨个加边(a, b),用并查集来维护连通块,由于是树,所以不存在边(a, b)(其中find(a) = find(b),每次只需要将sum += cnt1 * cnt2 * w(cnt1,2是a,b所在连通块的点数,w是(a, b)的边权)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define int long long
const int N = 100010;
int p[N], cnt[N];
int n;
struct edge{
int a, b, w;
bool operator<(const edge &e){
return w < e.w;
}
};
vector<edge> v;
int find(int x){
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
signed main(){
cin >> n;
for(int i = 1; i < n; i ++){
int a, b, w;
cin >> a >> b >> w;
v.push_back({a, b, w});
}
sort(v.begin(), v.end());
for(int i = 1; i <= n; i ++) p[i] = i, cnt[i] = 1;
int res = 0;
for(auto e : v){
int ra = find(e.a), rb = find(e.b);
p[rb] = ra;
res += cnt[ra] * cnt[rb] * e.w;
cnt[ra] += cnt[rb];
}
cout << res << endl;
return 0;
}