Codeforces Round 881 (Div. 3) 题解

A

题意:

给定一个数组,每个元素都需要一个颜色染色。每个颜色的得分为染色元素的最大值与最小值的差值,总得分为所有颜色的得分和

解析:

对于一个颜色,染色一个元素得分为 0 0 0,染色三个及以上时,除了最大元素与最小元素,其余元素均无贡献,所以一个颜色应该染色两个元素。

对于两种颜色 p , q p,q p,q,四个元素 a ≤ b ≤ c ≤ d a \le b \le c \le d abcd,可以证明 ( a , d ) , ( b , c ) (a,d),(b,c) (a,d),(b,c) 一定是最优的。

代码:

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, a[maxn];
void solve(){
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	sort(a+1, a+1+n);
	int ans = 0;
	for(int i = 1; i <= (n >> 1); i++)
		ans += a[n-i+1] - a[i];
	cout << ans << endl;
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}


B

解析:

注意到 0 = − 0 0 = -0 0=0,所以正数将序列分为多段,每一段内如果有负数就进行一次操作。

代码:

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e6+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
#define int ll
int a[maxn], n;
void solve(){
	cin >> n;
	int res = 0, len = 0, cnt = 0;
	for(int i = 1; i <= n; i++)
		cin >> a[i], res += fabs(a[i]);

	for(int i = 1; i <= n; i++){
		if(a[i] < 0)
			len++;
		else if(a[i] > 0){
			cnt += (len > 0 ? 1 : 0);
			len = 0;	
		}		
	}
	cnt += (len > 0 ? 1 : 0);
	cout << res << " " << cnt << endl;
	return;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}


C

解析:

满二叉树,节点 u u u 的左儿子为 2 u 2u 2u,右儿子为 2 u + 1 2u+1 2u+1 。节点 v v v 的父节点为 ⌊ v 2 ⌋ \lfloor\frac{v}{2}\rfloor 2v

代码:

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

void solve(){
	ll n, res = 0;
	cin >> n;
	while(n){
		res += n;
		n = n >> 1;
	}
	cout << res << endl;
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}


D

解析:

u u u 节点的苹果可以从以 u u u 为根的子树内的叶子节点掉落。

所以预处理每棵子树内的叶子节点数目,叶子节点的度为 1 1 1 。然后可以 O ( 1 ) O(1) O(1) 回答

代码:

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 2e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int head[maxn], tot;
struct edge{
	int to, nxt;
}e[maxn << 1];
int n, q, a, b;
int deg[maxn], cnt[maxn];
void add(int a, int b){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	head[a] = tot;
}
void dfs(int u, int fa){
	if(deg[u] == 1)
		cnt[u] = 1;
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == fa)
			continue;
		dfs(v, u);
		cnt[u] += cnt[v];
	}
}
void solve(){
	cin >> n;
	for(int i = 1; i <= n; i++)
		head[i] = deg[i] = cnt[i] = 0; 
	tot = 0;
	
	deg[1] = 1;
	for(int i = 1; i < n; i++){
		cin >> a >> b;
		add(a, b);
		add(b, a);
		deg[a]++;
		deg[b]++;
	}
	dfs(1, 0);
	cin >> q;
	while(q--){
		cin >> a >> b;
		ll res = 1ll * cnt[a] * cnt[b];
		//cout << "ans = ";
		cout << res << endl;
	}
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}


E

解析:

注意到答案具有单调性,所以可以二分答案。

c h e c k check check 的时候,前缀和统计 1 1 1 的数量,然后遍历每个区间判断。

代码:

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, m, q;
int op[maxn];
pii s[maxn];

bool check(int x){
	vector<int> a(n+1, 0);
	vector<int> sum(n+1, 0);
	for(int i = 1; i <= x; i++)
		a[op[i]] = 1;
	for(int i = 1; i <= n; i++)
		sum[i] = sum[i-1] + a[i];
	
	for(int i = 1; i <= m; i++){
		int l = s[i].fi;
		int r = s[i].se;
		
		if(sum[r] - sum[l-1] >= (r-l+1)/2+1)
			return true;
	}
	return false;
} 
void solve(){
	cin >> n >> m;
	for(int i = 1; i <= m; i++)
		cin >> s[i].fi >> s[i].se;
	cin >> q;
	for(int i = 1; i <= q; i++)
		cin >> op[i];
	int ans = -1;
	int l = 1, r = q;
	while(l <= r){
		int mid = (l+r) >> 1;
		if(check(mid)){
			r = mid-1;
			ans = mid;
		}
		else
			l = mid + 1;
	}
	//cout << "ans = ";
	cout << ans << endl;
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}


F1

解析:

首先注意到权值只有 1 , − 1 1,-1 1,1,子区间和的取值是连续的。即如果子区间和最大值为 d m a x dmax dmax,最小值为 d m i n dmin dmin,所有 d m i n ≤ d ≤ d m a x dmin \le d \le dmax dminddmax d d d 都以找到一个子区间,这个子区间的和为 d d d

简单版本询问路径的一个端点一定是根节点,所以可以统计根节点到每个节点路径上的最大区间和与最小区间和。

代码:

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 2e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n;
int dmin[maxn][2], dmax[maxn][2];
void solve(){
	cin >> n;
	string op;
	int u, x, k, tot = 1;
	dmin[1][1] = dmax[1][1] = 1;
	for(int i = 1; i <= n; i++){
		cin >> op;
		if(op == "+"){
			cin >> u >> x;
			++tot;
			dmin[tot][0] = min(dmin[u][0], dmin[u][1]);
			dmin[tot][1] = min(x, dmin[u][1]+x);
			
			dmax[tot][0] = max(dmax[u][0], dmax[u][1]);
			dmax[tot][1] = max(x, dmax[u][1]+x);
			
		}
		else{
			cin >> u >> x >> k;
			int l = min(dmin[x][0], dmin[x][1]);
			int r = max(dmax[x][0], dmax[x][1]);
			
			if(l <= k && k <= r)
				cout << "YES" << endl;
			else
				cout << "NO" << endl;
		}
	}
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}


F2

解析:

树链剖分,转换成 d f s dfs dfs 序,线段树维护区间信息。

关于线段树维护区间信息,可以看这道题 P4513 小白逛公园

但最大/最小字段和区间不满足交换律,在 lca 处合并时交换一下信息。 调试了好久都没发现这个问题,看了 大佬的文章 才知道的。

代码:

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 2e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
inline int ls(int x){return x << 1;}
inline int rs(int x){return x << 1 | 1;}
struct node{
	int lmax, rmax, maxv;
	int lmin, rmin, minv;
	int sum;
	node(){}
	node(int x){
		lmin = rmin = minv = lmax = rmax = maxv = sum = x;
	}
}t[maxn << 2];
struct edge{
	int to, nxt;
}e[maxn << 1];
struct ask{
	int u, v, k;
	ask(){}
	ask(int u, int v, int k) : u(u), v(v), k(k){}
};
int head[maxn], tot;
int siz[maxn], top[maxn], dep[maxn], fa[maxn], son[maxn];
int dfntot, dfn[maxn], pre[maxn];
int n, w[maxn], cnt;
void add(int a, int b){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	head[a] = tot;
}
void dfs1(int u, int p){
	fa[u] = p;
	siz[u] = 1;
	dep[u] = dep[p] + 1;
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == p)
			continue;
		dfs1(v, u);
		siz[u] += siz[v];
		if(siz[v] > siz[son[u]])
			son[u] = v;
	}
}
void dfs2(int u, int tp){
	top[u] = tp;
	dfn[u] = ++dfntot;
	pre[dfntot] = u;
	if(son[u])
		dfs2(son[u], tp);
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == fa[u] || v == son[u])
			continue;
		dfs2(v, v);
	}
}
void pushup(node &rt, node ls, node rs){
	rt.lmax = max(ls.lmax, ls.sum + rs.lmax);
	rt.rmax = max(rs.rmax, rs.sum + ls.rmax);		
	rt.maxv = max(ls.maxv, rs.maxv);
	rt.maxv = max(rt.maxv, ls.rmax + rs.lmax);
	
	rt.lmin = min(ls.lmin, ls.sum + rs.lmin);
	rt.rmin = min(rs.rmin, rs.sum + ls.rmin);	
	rt.minv = min(ls.minv, rs.minv);
	rt.minv = min(rt.minv, ls.rmin+ rs.lmin);
	
	rt.sum = ls.sum + rs.sum;
}

void build(int k, int l, int r){
	if(l == r){
		t[k] = node(w[pre[l]]);
		return;
	}
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
	pushup(t[k], t[ls(k)], t[rs(k)]);
}

node f(node a, node b){
	swap(a.lmin, a.rmin);
	swap(a.lmax, a.rmax);
	
	node res;
	pushup(res, a, b);
	return res;
}
node query(int k, int l, int r, int x, int y){
	if(x <= l && y >= r)
		return t[k];
	else if(y < l || x > r)
		return node(0);
	int mid = (l+r) >> 1;
	node res, lres, rres;
	if(x <= mid && y > mid){
		lres = query(ls(k), l, mid, x, y);
		rres = query(rs(k), mid+1, r, x, y);
		pushup(res, lres, rres);
		return res;
	}
	else if(y <= mid)
		return query(ls(k), l, mid, x, y);
	else
		return query(rs(k), mid+1, r, x, y);
}
void query(int x, int y, int d){
	node xres(0), yres(0);
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]]){
			swap(x, y);
			swap(xres, yres);
		}		
		node tmp1 = query(1, 1, cnt, dfn[top[x]], dfn[x]);
		node tmp2 = xres;
		pushup(xres, tmp1, tmp2);
		x = fa[top[x]];
	}
	if(dep[x] > dep[y]){ 
		swap(x, y);
		swap(xres, yres);
	}

	node tmp1 = query(1, 1, cnt, dfn[x], dfn[y]);
	node res;
	pushup(res, tmp1, yres);
	
	node ans = f(xres, res);
	
	int resmin = ans.minv;
	int resmax = ans.maxv;
	
	if(resmin <= d && d <= resmax)
		cout << "YES" << endl;
	else
		cout << "NO" << endl;
}
void init(){
	cnt = 1;
	tot = dfntot = 0;
	for(int i = 0; i <= n+5; i++)
		son[i] = head[i] = 0;
}
void solve(){
	cin >> n;
	string op;
	int v, x, u, k;
	w[1] = 1;
	init();
	vector<ask> q;

	for(int i = 1; i <= n; i++){
		cin >> op;
		if(op == "+"){
			++cnt;
			cin >> v >> x;
			add(cnt, v); add(v, cnt);
			w[cnt] = x;
		}
		else{
			cin >> u >> v >> k;
			q.push_back(ask(u, v, k));
		}
	}
	
	dfs1(1, 0);
	dfs2(1, 1);
	build(1, 1, cnt);
	for(int i = 0; i < q.size(); i++){
		int u = q[i].u;
		int v = q[i].v;
		int k = q[i].k;
		query(u, v, k);
	}
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}

你可能感兴趣的:(codeforces,c++,算法,图论,数据结构,动态规划)