C. Link Cut Centroids
**题意:**给定一棵树,若该树有两个重心,就删一条边,再连一条边,使得树的重心为一个。
**思路:**先求出一个重心,再根据重心的性质:若有两个重心,两重心相邻,且所有节点到重心的距离之和最小,若第二个重心存在,就求出,再将其中一个重心的儿子练到另外一个重心上,就可以了。其实我也不知道为什么,大胆猜想!!!
当时dfs里的continue写成了return,害得我wa了三发。。。。还以为猜错了呢
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define eps 1e-9
#define INF 0x3f3f3f3f
#define pb push_back
#define mk make_pair
#define fi first
#define se second
using namespace std;
typedef unsigned int UI;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> P;
const int N = 1e5 + 10;
struct edge{
int u, v;
int next;
}es[N * 2];
int head[N], cnt;
void add_edge(int u, int v) {
es[cnt].u = u;
es[cnt].v = v;
es[cnt].next = head[u];
head[u] = cnt++;
}
int n;
int vis[N];//重心标记
int siz[N], mson[N];//siz是子树的顶点大小,mson是最大子树的大小
//int dis[MAX_N]; //这个是距离数组,算路径时需要
int root, sum, tot, ans;//root用来更新重心
//sum是当前查询重心的子树的大小
//tot是表示距离数组的上界
void get_root(int u, int fa) { //v为当前节点(当前的根),fa为v的父亲, sn是查找重心的树的大小
siz[u] = 1; mson[u] = 0; //siz是以u为顶点的子树大小,mson是以u为分割点的最大子树
for (int i = head[u]; i != -1; i = es[i].next) {
int v = es[i].v;
if (v == fa || vis[v]) continue;//回到父亲或者下一节点是重心标记过了,直接跳过
get_root(v, u); //递归
siz[u] += siz[v]; //以u为根的子树大小
mson[u] = max(mson[u], siz[v]); //求最大子树
}
mson[u] = max(mson[u], sum - siz[u]); //当前最大子树和若以u为顶点的子树顶点-当前最大子树, 相比较
if (mson[u] < mson[root]) root = u; //最大子树的顶点数最小的子树
}
//在main函数中,第一次查重心时
//先将root置为0,将mson[0] = INF, 方便查找时更新
int dis;
void dfs(int u, int fa, int dep) {
dis += dep;
for (int i = head[u]; i != -1; i = es[i].next) {
int v = es[i].v;
if (v == fa) continue;
dfs(v, u, dep + 1);
}
}
int main () {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
cnt = 0;
for (int i = 0; i <= n; i++) {
head[i] = -1;
vis[i] = 0;
siz[i] = mson[i] = 0;
}
for (int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
sum = n;
root = 0, mson[0] = INF;
get_root(1, 0);
// printf("重心:%d\n", root);
dis = 0;
dfs(root, 0, 0);
int dd = dis;
bool f = true;
int root2;
for (int i = head[root]; i != -1; i = es[i].next) {
dis = 0;
int v = es[i].v;
dfs(v, 0, 0);
if (dis == dd) {
root2 = v;
f = false;
break;
}
}
if (f) {
int v = es[head[root]].v;
printf("%d %d\n", root, v);
printf("%d %d\n", root, v);
}
else {
int v;
for (int i = head[root]; i != -1; i = es[i].next) {
int p = es[i].v;
if (p != root2) v = p;
}
printf("%d %d\n", root, v);
printf("%d %d\n", root2, v);
}
}
return 0;
}
D. Three Sequence
题意: 给定一个数组 a a a , 自己构造两个数组 b b b 和 c c c,使得 a i = b i + c i a_i = b_i + c_i ai=bi+ci ,并且 b b b 为非严格递增, c c c 为非严格递减。 q q q 次询问,每次在区间 [ l , r ] + x [l,r] +x [l,r]+x 后求 m i n ( m a x ( b i , c j ) ) min(max(b_i, c_j)) min(max(bi,cj)) 。
思路: 因为 b n ≥ b i b_n \geq b_i bn≥bi 且 c 1 ≥ c i c_1 \geq c_i c1≥ci ,所以 m i n ( m a x ( b i , c j ) ) = m i n ( m a x ( b n , c 1 ) ) min(max(b_i,c_j)) = min(max(b_n,c_1)) min(max(bi,cj))=min(max(bn,c1))
构造思路: i ≥ 2 i \geq 2 i≥2 时,若 t p = a i − a i − 1 ≥ 0 tp= a_i - a_{i-1} \geq 0 tp=ai−ai−1≥0 ,就在 b i = b i − 1 + t p , c i = c i − 1 b_i = b_{i-1} + tp, c_i = c_{i-1} bi=bi−1+tp,ci=ci−1
若 t p < 0 tp < 0 tp<0 ,就 c i = c i − 1 + t p , b i = b i − 1 c_i = c_{i-1} + tp, b_i = b_{i-1} ci=ci−1+tp,bi=bi−1
这样 b n = ∑ i = 2 n ( a i − a i − 1 ) > 0 大于0的累加 b_n = \sum_{i=2}^{n}(a_i - a_{i-1})>0 \text{ 大于0的累加} bn=∑i=2n(ai−ai−1)>0 大于0的累加 ,所以 答案就是 m a x ( c 1 , a 1 − c 1 + b n ) max(c_1, a_1 - c1 + b_n) max(c1,a1−c1+bn)
即求的是 a 1 + ∑ i = 2 n ( a i − a i − 1 ) > 0 大于0的累加 a_1 + \sum_{i=2}^{n}(a_i - a_{i-1})>0 \text{ 大于0的累加} a1+∑i=2n(ai−ai−1)>0 大于0的累加,就是 a a a 的差分数组的第一项和之后大于0 的总和。每次询问只要 O ( 1 ) O(1) O(1) 维护差分数组,得到总和就能得到答案。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
int main() {
scanf("%d", &n);
vector<ll> a(n + 2);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
vector<ll> f(n + 2);
f[1] = a[1];
for (int i = 2; i <= n; i++) {
f[i] = a[i] - a[i - 1];
}
ll sum = a[1];
for (int i = 2; i <= n; i++) {
if (f[i] > 0) sum += f[i];
}
ll ans;
if (sum >= 0) {
ans = sum / 2 + sum % 2;
}
else {
ans = sum / 2;
}
printf("%lld\n", ans);
int q;
scanf("%d", &q);
while (q--) {
int l, r;
ll x;
scanf("%d%d%lld", &l, &r, &x);
ll tl = f[l];
ll tr = f[r + 1];
if (x != 0) {
f[l] += x;
f[r + 1] -= x;
if (x > 0) {
if (l == 1) sum += x;
if (tl <= 0 && f[l] > 0 && l != 1) sum += f[l];
else if (tl > 0 && l != 1) sum += x;
if (r + 1 <= n) {
if (tr > 0) sum -= min(x, tr);
}
}
else {
if (l == 1) sum += x;
if (tl > 0 && l != 1) sum -= min(tl, -x);
if (r + 1 <= n) {
if (tr <= 0 && f[r + 1] > 0) sum += f[r + 1];
else if (tr > 0) sum -= x;
}
}
}
if (sum >= 0) {
ans = sum / 2 + sum % 2;
}
else {
ans = sum / 2;
}
printf("%lld\n", ans);
}
return 0;
}