筱翼深凉
2021/05/25阅读:61主题:嫩青
codeforces题解
我的博客[1]
题目链接[2]
这次题目其实蛮坑的,有点恶心人。
A. Eshag Loves Big Arrays[3]
题意
给定一个长度为 的序列,可以选择一段区间将其中严格大于区间平均数的数删掉,可以执行任意多次,问最后得到的序列是什么样的。
思路
由题意不难得出我们删到最后一定只剩下数组中的最小值,因为最小值是永远小于等于区间平均值,所以删到最后就只剩下了最小值。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 110;
int n;
int g[N];
bool st[N];
int main()
{
int T;
cin >> T;
while (T -- )
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> g[i], st[i] = false;
int res = 0;
int minv = 10000;
for (int i = 1; i <= n; i ++ ) minv = min(minv, g[i]);
for (int i = 1; i <= n; i ++ )
if (g[i] == minv) res ++ ;
cout << n -res << endl;
}
return 0;
}
B. Eshag Loves Big Arrays[4]
题意
给定一个长度为 的序列,要求选择出其最长的满足 。
思路
我们考虑一下情况:
-
当 时,原式等价于 ,不成立; -
当 时,原式等价于 ,成立; -
当 时,原价等价于 ,成立。
所以对于一个子序列,一定包含原序列的所有非正数,以及最多一个正数,若有正数,正数选择最小的一定是最优的。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 100010;
int n;
int g[N];
int s[N];
int main()
{
int T;
cin >> T;
while (T -- )
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> g[i];
int res = 0;
sort(g + 1, g + 1 + n);
int t = 0;
for (int i = 1; i <= n; i ++ )
if (g[i] <= 0) res ++ ;
int minv = 0x3f3f3f3f;
for (int i = 2; i <= n; i ++ )
if (g[i] <= 0)
{
minv = min(minv, g[i] - g[i - 1]);
}
for (int i = 1; i <= n; i ++ )
if (g[i] > 0) // 特判正数是否可以选择
{
if (minv >= g[i]) res ++ ;
break;
}
cout << res << endl;
}
return 0;
}
C. Parsa's Humongous Tree[5]
题意
给定一棵树,树上每个节点 都包含一个值域 ,要求每个节点在其值域中选择一个数 ,使得所有的边 有 最大。
思路
易只当只有两个点时选择边界一定是最有的。 三个点时设点 点 点 ,则有:
-
,不难知则此时最优选择一定是 ; -
,此时最优选择是 ; -
,此时最优选择是 ; -
,此时最优选择是 或者 。
(证明并不难,在此不多赘述)由此不难看出选择边界一定是最优的情况,并且选择边界我们就可有两个点转移到三个点,再转移到更多点,即树形 。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 100100, M = 200100;
int n;
int h[N], e[M], ne[M], idx;
int l[N], r[N];
LL f[N][2];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u, int fa)
{
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
dfs(j, u);
f[u][0] += max(f[j][0] + abs(l[u] - l[j]), f[j][1] + abs(l[u] - r[j]));
f[u][1] += max(f[j][0] + abs(r[u] - l[j]), f[j][1] + abs(r[u] - r[j]));
}
}
int main()
{
int T;
cin >> T;
while (T -- )
{
idx = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &l[i], &r[i]), h[i] = -1, f[i][1] = f[i][0] = 0;
for (int i = 1; i < n; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1, -1);
cout << max(f[1][1], f[1][0]) << endl;
}
return 0;
}
D. Kavi on Pairing Duty[6]
题意
在 的数轴上,要求选择任意两个点对点对 使得 或者 包含于 ,求出点对的选择有多少种。
思路
线性 ,证明过程比较繁杂,这里只做简单叙述,读者看完状态转移方程就可理解。 状态表示为 表示从 区间的点对选择。
-
包含情况,对于区间 ,当我们将两边点按等长都选择上之后,我们中间的点就可以随便选取,例如选择点对 ,则中间的区间 可以随便选取即 ,选择点对 ,则中间的区间 可以随便选取即 ,依次类推; -
等长情况,区间长度能被点对长度整除,就能选择,即 的正约数集合。
综上所述,状态转移方程为: 。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 1000010, mod = 998244353;
int n;
int dp[N];
int v[N];
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
for (int j = i; j <= n; j += i)
v[j] ++ ;
LL sum = 1;
dp[1] = 1;
for (int i = 2; i <= n; i ++ )
{
dp[i] = (sum + v[i]) % mod;
sum = (sum + dp[i]) % mod;
}
cout << dp[n] << endl;
return 0;
}
参考资料
戳这里: https://www.cnblogs.com/wanshu/p/14807034.html
[2]戳这里: https://codeforces.com/contest/1529
[3]戳这里: https://codeforces.com/contest/1529/problem/A
[4]戳这里: https://codeforces.com/contest/1529/problem/B
[5]戳这里: https://codeforces.com/contest/1529/problem/C
[6]戳这里: https://codeforces.com/contest/1529/problem/D
作者介绍