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;
}