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数组的最大长度

解析:

  1. 如果\(a\)中元素非正数,毫无疑问都是符合\(strange\)条件的,现在问题就转换成了对于正数部分该如何处理。显然如果某个正数的添加使得必须要清除负数,这样是不划算的(因为这样使得MAX变大了,但数组长度没变),而且在成功添加的情况下,正数越大约不划算
  2. 于是贪心策略就是,把所有负数收入囊中,并维护一个相邻的最小差的绝对值,如果无法插入就输出答案,否则添加
#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)

题目链接
⭐⭐

题意:
给出一个树,以及每个节点之间的取值范围,现在对每个节点取某个值,使得相邻节点值的差绝对值的和最大

解析:

  1. 不论每个节点何种取值,不难发现,只有取得某个节点的端点值时,才有可能获得最大的绝对值的和
  2. 定义状态\(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\)个点对,保证任意两个点对的距离都相等,或者一个点对被包含在另一个点对中,询问合法的有多少种点对选择方式

解析:

  1. 当选择点对左边选择第一个点时,考虑右边选择的点的位置大于\(n\)时的情况,例如选择第\(2n\)个点时,那就还有\(n-1\)个点对,同理选择第\(2n-1\)个点时,对于左端第二个点也可以选择一个相同长度的点对,这样就还有\(n-2\)个点对……,所以这部分的贡献值为\(\sum\limits_{i=0}^{n-1}dp[i]\)
  2. 当选择的点的位置小于等于\(n\)时,假定这个点之前的点为\(x\),则构成\((1,x+1),(2,x+2),\dots(x,2x)\)的点对集小整体,也就是说这一个整体每个点对长度都为\(x\),且整体长度为\(2x\),那只要保证\(2x|2n\)即可,所以这部分贡献是\(\sum_{i|n} dp[i]\)
  3. 对于第一部分可以使用前缀和进行统计,第二部分可以对每个因子统计贡献计算获得
#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+树+贪心)

题目链接
⭐⭐⭐⭐

题意:
给出两个树,找到最大的点集使得在一号树中在点集内的所有点要求在一条链上,在二号树中在点集内的任意两个点不能一个点是另一个点的祖宗

解析:

  1. 首先考虑对二号树进行\(dfs\),求得每个节点对应的子树区间范围\((l,r)\)
  2. 考虑直接对一号树进行\(dfs\),在\(dfs\)的过程中维护当前已经探索到的点集,这样可以保证当前点集在一条链上,那么现在对于一个新探索到的点,考虑几种情况来判断是否可以加入点集
    • 如果新的点是点集中某个点的子树内的一点,那么可以贪心的用新点代替旧点,因为这样的情况下点集大小不变,但是所覆盖的点的范围减少了
    • 如果新的点是点集中某个点的祖宗,则与第一种情况同样分析,这时候不加这个点是最贪心的
    • 如果这个点和点集内所有点没有交集,则加入这个点
  3. 问题转化成了如何判断点与点的亲缘关系,可以通过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);
	}
}
posted @ 2021-05-29 10:38  DreamW1ngs  阅读(49)  评论(0编辑  收藏  举报