Codeforces Round #670 (Div. 2) C.Link Cut Centroids,D.Three Sequencest题解

1406C. Link Cut Centroids

题意:给一颗树,定义树的质心为:将质心及质心所连的所有边剔除之后,生成的联通块的大小的最大值最小的点。要求删除一条边,再加入一条边,使得新图仍然是树且质心唯一。

思路:通过质心的定义就能够明白树的质心至多2个,且若质心有两个,这两个质心必定是父子节点的关系。那么这个问题就很好解决了,只有两种可能:1.质心只有一个。那么只要不改变树本身的样子,即删除、添加任意的一条边,质心仍然唯一;2.质心有两个那么只要将两个质心的其中一个质心按照剔除的规则生成的最大连通块给破坏掉就只会剩下另一个质心了,所以要特别考虑的就是如何破坏其中一个质心可以想到,两个质心被剔除的时候生成的最大连通块都是以另一个质心为根的子树,因此只要将其中一个质心的子树的任意一个叶子结点连接到另一个质心的子树上,就算是成功破坏了这个质心了。最简单的方法当然就是再来一个DFS找到A质心的叶子结点然后和B质心连接。

以下说明代码:

声明数组:1.ans[] :ans[i] 表示将 i 结点以及 i 结点所连的边删除得到新森林的最大连通块的大小。

                  2.sz[] :sz[i] 表示以 i 结点为根的子树的大小。

                  3.son[]son[i] 表示 i 结点的子结点中sz[] 最大的结点

根据质心的定义,就可以知道ans数组的计算方法:ans[u] = max(n - sz[u], sz[son[u]])

那么只要再一个for循环遍历ans数组就可以知道最小的ans是否存在两个,即是否有2个质心。

AC代码:

/*---------------------------------
 *File name: A.cpp
 *Creation date: 2020-09-22 19:34
 *Link: 
 *-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int> 
using namespace std;
typedef int readtype;
const int maxn = 1e5 + 5;
const int inf = INT_MAX;
const LL mod = 1e9 + 7;

inline readtype read(){
	readtype X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}

struct Node{
	int v, nxt;
}edge[maxn << 1];
int head[maxn], tot = 0;

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].nxt = head[u];
	head[u] = tot++;
} 

int sz[maxn];
int son[maxn];
int pre[maxn];
int ans[maxn];
int n;

void Dfs(int u, int p = -1){
	sz[u] = 1;
	pre[u] = p;
	for(int i = head[u]; i != -1; i = edge[i].nxt){
		int v = edge[i].v;
		if(v == p) continue;
		Dfs(v, u);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;
	}
	ans[u] = max(n - sz[u], sz[son[u]]);
	//printf("%d: sz = %d, son = %d, ans = %d\n", u, sz[u], son[u], ans[u]);
}

int leaf;

void Dfs1(int u, int p){
	bool flag = 0;
	for(int i = head[u]; i != -1; i = edge[i].nxt){
		int v = edge[i].v;
		if(v == p) continue;
		Dfs1(v, u);
		flag = 1;
		break;
	}
	if(!flag) leaf = u;
}

int main(){
	int t = read();
	while(t--){
		n = read();
		for(int i = 1; i <= n; ++i) head[i] = -1, son[i] = 0; tot = 0;
		for(int i = 1; i < n; ++i){
			int x = read(), y = read();
			Add_Edge(x, y);
			Add_Edge(y, x);
		}
		Dfs(1);
		int Min = ans[1], nMin = inf, x = 1, y = -1;
		for(int i = 2; i <= n; ++i){
			if(Min >= ans[i]){
				nMin = Min;
				Min  = ans[i];
				y = x;
				x = i;
			}
		}
		//printf("Min = %d, nMin = %d,x = %d, y= %d\n", Min, nMin, x, y);
		if(Min == nMin){
			if(pre[x] == y) swap(x, y);
			Dfs1(y, x);
			printf("%d %d\n", leaf, pre[leaf]);
			printf("%d %d\n", leaf, x);
		}
		else{
			printf("%d %d\n", 1, edge[head[1]].v);
			printf("%d %d\n", 1, edge[head[1]].v);
		}
	}
	return 0;
}

1406D. Three Sequences

题意:给一个长度为N的数组A,要求构造两个新的长度为N的数组B、C,且满足以下要求:1.B[i] + C[i] = A[i](\forall i \epsilon [1,N]);2.B数组为非递减数组;3.C数组为非递增数组。对A数组将会有Q次修改,给出L,R,X,每次修改A[i] += x(\forall i\epsilon [L,R])。要求输出修改前以及每一次修改后的max(B[i], C[i])。

思路:由于B、C数组是并非是严格单调的数组,因此可以想到,A[i] 比起 A[i - 1] 的大小变化,可以在不修改B[i - 1]或C[i - 1]的情况下就得到新的B[i], C[i],即:若当A[i] > A[i - 1] ,那么只需要让B[i] = B[i - 1] + (A[i] - A[i - 1])C[i] = C[i - 1],反之亦然,则可以构造出BC数组,且必定满足三个要求。而要求输出的Max(B_i, C_i),由于BC具有单调性,实际上是输出Max(B_n, C_1)即可。对于初始数组A,我们若只关注B数组的增加,那么可以知道B_nB_1之间的差距为:\sum_{i = 2}^{n}max(0,(A[i] - A[i - 1])),若将此表达式记为 sum,那么B_N = B_1 + sum, 将C_1设为X,那么B_1 = A_1 - C_1 = A_1 - XB_N = B_1 + sum = A_1 - X + sum,因此要输出的即为:Max(B_N, C_1) = Max(A_1 - X + sum, X),可以看到这个表达式中只有一个变量X,且Max中的两个内容为负相关。因此当A_1 - X + sum = X时,Max(A_1 - X + sum, X)最小,即X = (A_1 + sum) / 2

那么现在考虑修改时要如何快速计算输出值:观察X的式子可以看出X仅与A_1sum相关。再根据sum的计算公式可以明白,每次对A数组的区间修改,实际上对sum的影响只有A_l - A_{l - 1}A_{r + 1} - A_r的变化。所以每次修改A数组时只需要重新计算这两处对sum的贡献就可以得到新的X,进一步得到B_N,C_1的值。

优化:将A_i改变为A_{i} - A_{i - 1},则对sum的影响就是A_l 和A_{r + 1},因此每次修改都只要更新这两处的值。总复杂度为O(N + Q)

AC代码:

/*---------------------------------
 *File name: A.cpp
 *Creation date: 2020-09-22 19:34
 *Link: 
 *-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int> 
using namespace std;
typedef long long readtype;
const int maxn = 1e5 + 5;
const int inf = INT_MAX;
const LL mod = 1e9 + 7;

inline readtype read(){
	readtype X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}

vector<LL> a;

inline void Update(LL &sum, int pos, int x){
	if(pos == a.size()) return;
	if(pos == 1) {
		a[1] += x;
		return;
	}
	sum -= max(0LL, a[pos]);
	a[pos] += x;
	sum += max(0LL, a[pos]);
}

int main(){
	int n = read();
	a.resize(n + 1);
	LL sum = 0;
	for(int i = 1; i <= n; ++i) a[i] = read();
	for(int i = n; i >= 2; --i) a[i] = a[i] - a[i - 1], sum += max(0LL, a[i]);
	LL c = ceil((a[1] + sum) / 2.0);
	LL b = a[1] - c;
	printf("%lld\n", c);
	int q = read();
	while(q--){
		int l = read(), r = read(), x = read();
		Update(sum, l, x);
		Update(sum, r + 1, -x);
		c = ceil((a[1] + sum) / 2.0);
		b = a[1] - c;
		printf("%lld\n", c);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值