Codeforces Round #722 (Div. 2)
A(水题)
题目链接
⭐
题意:
给出一组序列,每次可以将大于当前数组平均数的数删去,问最多可以删去多少个数
解析:
非常显然,由于最小值会一直拉低平均值使得其一直小于最大值,所以只有最小值会被保留
#include<bits/stdc++.h>
using namespace std;
int dat[10005];
int main() {
int T, n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
int mn=0x3f3f3f3f;
for (int i = 0; i < n; ++i)
scanf("%d", &dat[i]), mn=min(mn,dat[i]);
int t = n;
for (int i = 0; i < n; ++i)
if (dat[i] == mn) --t;
printf("%d\n", t);
}
}
B(贪心)
题目链接
⭐
题意:
定义数组\(b\)中任意两元素的差的绝对值小于等于数组内的最大值,则称这个数组是strange的。现在给出数组\(a\),求\(a\)的子序列构成strange数组的最大长度
解析:
- 如果\(a\)中元素非正数,毫无疑问都是符合\(strange\)条件的,现在问题就转换成了对于正数部分该如何处理。显然如果某个正数的添加使得必须要清除负数,这样是不划算的(因为这样使得MAX变大了,但数组长度没变),而且在成功添加的情况下,正数越大约不划算
- 于是贪心策略就是,把所有负数收入囊中,并维护一个相邻的最小差的绝对值,如果无法插入就输出答案,否则添加
#include<bits/stdc++.h>
using namespace std;
int dat[100005];
int main() {
int T, n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", &dat[i]);
sort(dat, dat + n);
int i;
int mn = INT32_MAX;
int ans = 0;
for (i = 0; i < n; ++i) {
if (i) mn = min(mn, dat[i] - dat[i - 1]);
if (dat[i] > mn) break;
++ans;
}
printf("%d\n", ans);
}
}
C(树上dp)
题目链接
⭐⭐
题意:
给出一个树,以及每个节点之间的取值范围,现在对每个节点取某个值,使得相邻节点值的差绝对值的和最大
解析:
- 不论每个节点何种取值,不难发现,只有取得某个节点的端点值时,才有可能获得最大的绝对值的和
- 定义状态\(dp[i][0]\),代表第\(i\)个节点选择左端点时的最大值,以及\(dp[i][1]\),代表第\(i\)个节点选择右端点时的最大值,那不难得到状态转移式
\[\begin{cases}
dp[u][0]=\max(dp[v][0]+|l[u]-l[v]|,dp[v][1]+|l[u]-r[v]|) \\
dp[u][1]=\max(dp[v][0]+|r[u]-l[v]|,dp[v][1]+|r[u]-r[v]|)
\end{cases}
\]
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
vector<int> e[maxn];
int l[maxn], r[maxn];
long long dp[maxn][2];
void dfs(int fa, int cur) {
for (auto& i : e[cur]) {
if (i == fa) continue;
dfs(cur, i);
dp[cur][0] += max(dp[i][0] + abs(l[cur] - l[i]), dp[i][1] + abs(l[cur] - r[i]));
dp[cur][1] += max(dp[i][0] + abs(r[cur] - l[i]), dp[i][1] + abs(r[cur] - r[i]));
}
}
int main() {
int T, n,a,b;
scanf("%d", &T);
while (T--) {
memset(dp, 0, sizeof(dp));
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &l[i], &r[i]);
e[i].clear();
}
for (int i = 1; i < n; ++i)
scanf("%d%d", &a, &b), e[a].push_back(b), e[b].push_back(a);
dfs(0, 1);
printf("%lld\n", max(dp[1][0], dp[1][1]));
}
}
D(dp)
题目链接
⭐⭐⭐
题意:
在一个长度为\(2n\)的轴上,选择\(n\)个点对,保证任意两个点对的距离都相等,或者一个点对被包含在另一个点对中,询问合法的有多少种点对选择方式
解析:
- 当选择点对左边选择第一个点时,考虑右边选择的点的位置大于\(n\)时的情况,例如选择第\(2n\)个点时,那就还有\(n-1\)个点对,同理选择第\(2n-1\)个点时,对于左端第二个点也可以选择一个相同长度的点对,这样就还有\(n-2\)个点对……,所以这部分的贡献值为\(\sum\limits_{i=0}^{n-1}dp[i]\)
- 当选择的点的位置小于等于\(n\)时,假定这个点之前的点为\(x\),则构成\((1,x+1),(2,x+2),\dots(x,2x)\)的点对集小整体,也就是说这一个整体每个点对长度都为\(x\),且整体长度为\(2x\),那只要保证\(2x|2n\)即可,所以这部分贡献是\(\sum_{i|n} dp[i]\)
- 对于第一部分可以使用前缀和进行统计,第二部分可以对每个因子统计贡献计算获得
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
long long dp[maxn];
const long long mod = 998244353;
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
for (int j = 2 * i; j <= n; j += i)
++dp[j];
dp[0] = 1;
for (int i = 1; i <= n; ++i)
dp[i] = (2 * dp[i - 1] + dp[i]) % mod;
printf("%lld", (dp[n] - dp[n - 1] + mod) % mod);
}
E(dfs+树+贪心)
题目链接
⭐⭐⭐⭐
题意:
给出两个树,找到最大的点集使得在一号树中在点集内的所有点要求在一条链上,在二号树中在点集内的任意两个点不能一个点是另一个点的祖宗
解析:
- 首先考虑对二号树进行\(dfs\),求得每个节点对应的子树区间范围\((l,r)\)
- 考虑直接对一号树进行\(dfs\),在\(dfs\)的过程中维护当前已经探索到的点集,这样可以保证当前点集在一条链上,那么现在对于一个新探索到的点,考虑几种情况来判断是否可以加入点集
- 如果新的点是点集中某个点的子树内的一点,那么可以贪心的用新点代替旧点,因为这样的情况下点集大小不变,但是所覆盖的点的范围减少了
- 如果新的点是点集中某个点的祖宗,则与第一种情况同样分析,这时候不加这个点是最贪心的
- 如果这个点和点集内所有点没有交集,则加入这个点
- 问题转化成了如何判断点与点的亲缘关系,可以通过1中计算的\((l,r)\),给出某个点\(u\),以及他的子树中的一点\(v\),一定有\(l[u]\le l[v],r[u]\ge r[v]\),并且很容易得知,对于一个树而言,所有的节点的\((l,r)\)只有包含关系与不相关两种情况,所以当得知\(l[a]\le l[b],r[a]\le r[b]\)时,肯定能得出\(a,b\)不相关
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 5;
vector<int> e1[maxn], e2[maxn];
int l[maxn], r[maxn];
int cnt,ans;
typedef pair<int, int> P;
set<P> s;
void dfs2(int cur) {
l[cur] = ++cnt;
for (auto& i : e2[cur]) dfs2(i);
r[cur] = cnt;
}
int insert(int x) {
auto i = s.lower_bound(P(l[x],x));
if (i != s.end() && r[i->second] <= r[x]) return 0;
if (i == s.begin()) return -1;
--i;
int t = i->second;
if (r[x] > r[t]) return -1;
s.erase(i);
return t;
}
void dfs1(int cur) {
int res = insert(cur);
if (res) s.insert(P(l[cur], cur));
ans = max(ans, (int)s.size());
for (auto& i : e1[cur]) dfs1(i);
if (res) {
s.erase(P(l[cur], cur));
if (~res) s.insert(P(l[res], res));
}
}
int main() {
int T, n, t;
scanf("%d", &T);
while (T--) {
cnt =ans= 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) e1[i].clear(), e2[i].clear();
for (int i = 2; i <= n; ++i)
scanf("%d", &t), e1[t].push_back(i);
for (int i = 2; i <= n; ++i)
scanf("%d", &t), e2[t].push_back(i);
dfs2(1);
dfs1(1);
printf("%d\n", ans);
}
}
努力变成更好的自己吧!