Codeforces Round #722 (Div. 2)

A

题目描述

给定一个数列\(A\),每次选一个子序列算平均值并且删掉比平均值大的元素,问最多删去几个值?

数据范围

\(n \leq 100,a_i \leq 100\)

分析

最后一定剩一个最小元素,排序输出即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 110;

int a[N];

int main () {
	int T;
	cin >> T;
	while(T --) {
		int n;
		int pos = -1;
		cin >> n;
		for(int i = 1;i <= n; ++i) cin >> a[i];
		sort(a + 1,a + n + 1);
		int tmp = a[1];
		for(int i = 1;i <= n; ++i) {
			if(a[i] != tmp) {
				//cout << "#:" << i << endl;
				pos = i;
				break;
			}
		}
		if(pos == -1) cout << 0 << endl;
		else cout << n - pos + 1 << endl;
	}
	return 0;
}

B

题目描述

给定一个数组\(B\),求一个最大集合使得集合中任意一对元素\((u,v)\)满足\(|u - v| >= max_S\)

数据范围

\(\sum n \leq 10^5\)

分析

将数组从小到大排序,然后记录一个当前集合中的点对元素距离最小值,计算结果输出即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T;
int n;
int a[N];
int main () {
	cin >> T;
	while(T --) {
		cin >> n;
		for(int i = 1;i <= n; ++i) {
			cin >> a[i];
		}
		sort(a + 1,a + n + 1);
		int ans = 1;
		int lm = INT_MAX;
		for(int i = 2;i <= n; ++i) {
			int tmp = abs(a[i] - a[i - 1]);
			lm = min(lm,tmp);
			if(lm < a[i]) break;
			else ans ++;
		}
		cout << ans << endl;
	}
	return 0;
}

C

题目描述

给定一个树,每个节点可以选的值为\([l_i,r_i]\)之间的一个值,求如何安排数值可以使得\(\sum_{u,v \in T} |a_u - a_v|\)最大,输出最大值。

数据范围

\(\sum n \leq 10^5,T \leq 250,1 \leq l_i,r_i \leq 10^9\)

分析

对于任意连接的一个点对\((u,v)\)来说,最大值一定出现在端点处,我们考虑设\(f_{i,0/1}\)表示当处理到第\(i\)个节点时,当前节点选\(l_i\)\(r_i\)的最大收益,转移为:

f[u][0] += max(f[v][0] + abs(l[u] - l[v]),f[v][1] + abs(l[u] - r[v]));
f[u][1] += max(f[v][0] + abs(r[u] - l[v]),f[v][1] + abs(r[u] - r[v]));

开个\(long \; \;long\)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
ll f[N][2];//选上界,or选下界

struct edge {
	int to;
	int nxt;
}e[N << 1];
int cnt;
int head[N];
void add (int u,int v) {
	e[++cnt].to = v;
	e[cnt].nxt = head[u];
	head[u] = cnt;
	return;
}

void Addtree(int u,int v) {
	add(u,v),add(v,u);
}
int T;
int n;
int l[N],r[N];

void dfs(int x,int fa) {
	for(int i = head[x];i;i = e[i].nxt) {
		int y = e[i].to;//cout << "#:" << y << endl;
		if(y == fa) continue;
		dfs(y,x);
		f[x][0] += max(f[y][0] + abs(l[x] - l[y]),f[y][1] + abs(r[y] - l[x]));
		f[x][1] += max(f[y][0] + abs(r[x] - l[y]),f[y][1] + abs(r[y] - r[x]));
	}
}


int main () {
	ios :: sync_with_stdio(false);
	cin >> T;
	while(T --) {
		cin >> n;
		for(int i = 1;i <= n; ++i) cin >> l[i] >> r[i];
		cnt = 0;
		memset(head,0,sizeof head);
		memset(f,0,sizeof f);
		for(int i = 1;i < n; ++i) {
			int u,v;
			cin >> u >> v;
			Addtree(u,v);
		}
		dfs(1,0);
		//cout << "#:" << endl;
		//for(int i = 1;i <= n; ++i) cout << f[i][0] << ' ' << f[i][1] << endl;
		cout << max(f[1][0],f[1][1]) << endl;
	}
	return 0;
}

D

题目描述

给定\(2n\)个点,求可以形成\(n\)个点对使得满足:

  • 大小相同
  • 互相包含

的方案数。

数据范围

\(n \leq 10^6\)

分析

这道题还是很考察思维能力的。

先考虑这个题如何递推下去,对于一种长度为\(2i\)的情况,如果想变成\(2i+2\)的情况,就需要在两边填上两个点,中间变为“包含”情况。

考虑大小相同的情况如何计算:

  • 因为你需要让\(n\)个点对用恰好\(2n\)个点,考虑枚举长度,经过验算发现,如果说长度为\(i\)的约数,那么必定存在一种大小相同的情况的局面,因此大小相同的情况为\(i\)的约数个数。

然后再考虑包含情况:

  • 根据递推,当\(i = 0\)的时候是无解的,因此此时答案为0.
  • \(f_i\)表示长度为\(2i\)情况的方案数,那么\(f_i += sum_{c_j}\)\(c为约数\),维护好\(sum\)值即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
#define ll long long
const int mod = 998244353;
ll f[N];//µÚi¸öµãµÄ½á¹û
int c[N];
int n;

void _div(int n) {
	for(int i = 1;i <= n; ++i) {
		for(int j = i;j <= n;j += i) {
			c[j] ++;
		}
	}
}
int main () {
	
	ll n;
	cin >> n;
	//vector <int> d;
	_div(n);
	ll sum = 0;
	for(int i = 1;i <= n; ++i) {
		//cout << v[i].size() << endl;
		
		f[i] = (sum + c[i]) % mod;
		sum = (sum + f[i]) % mod;
	}	
	cout << f[n] % mod << endl;
	return 0;
}

E

原文地址:https://www.cnblogs.com/akoasm/p/14809782.html