Ethflow Round 1 (Codeforces Round 1001, Div. 1 + Div. 2)


A. String

题意:给你一个01串,你每次选一个子序列,其中相邻的两位不相同,如何把这个子序列位置上的数取反,问多少次能变成全0。

因为必须是10交替,那么取反后相当于没操作。所以我们每次操作一个1就行。这样操作数是1的个数。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    std::cout << std::count(s.begin(), s.end(), '1') << "\n";
}

B. Clockwork

题意:有n个时钟,每个时钟有一个倒计时。你可以选择一个起点,每次往左边或者右边移动,每秒钟所有时钟都会减一,你可以把当前位置的时钟调到初始状态。问能不能永远不让任何一个时钟变为0。

因为我们不让任何一个时钟变为0,那么我们肯定是要走遍每一个位置。对于第i个时钟,最坏情况是往左走到尽头然后在走回来或者往右走到尽头再走回来。那么ai要大于这两段的距离。ai大于2(i1)2(ni)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }
    for (int i = 0; i < n; ++ i) {
    	if (i * 2 >= a[i] || (n - 1 - i) * 2 >= a[i]) {
    		std::cout << "NO\n";
    		return;
    	}
    }
    std::cout << "YES\n";
}

C. Cirno and Operations

题意:一个数组,每次可以让它变成它的差分数组或者把它翻转。你可以操作任意次。问可以得到的最大数组和是多少。

数组总和的原值为i=1nai,如果进行差分,数组和变为i=2naiai1=i=2naii=1n1ai。发现除了an其他都会减去,那么翻转操作可以让我们选择减去a1还是an。我们应该减去更小的那个。所以如果an<a1我们就翻转数组再差分。模拟n1次取每一次和的最大值就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<i64> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

	i64 ans = std::accumulate(a.begin(), a.end(), 0ll);
	for (int i = 0; i + 1 < n; ++ i) {
		if (a[0] < a.back()) {
			std::reverse(a.begin(), a.end());
		}

		i64 sum = 0;
		for (int i = 0; i + 1 < a.size(); ++ i) {
			a[i] = a[i] - a[i + 1];
			sum += a[i];
		}

		ans = std::max(ans, sum);
		a.pop_back();
	}
	std::cout << ans << "\n";
}


D. Balanced Tree

题意:给你一棵树,每个点有一个取值范围,你每次可以让一棵子树上的点都加一。要让所有节点值相同,问这个值最小是多少。

考虑一棵子树,我们假设值已经取好了,那么我们可以先让值比根节点小子节点的都变成根节点的值,然后这棵子树就只剩下了大于等于根节点的子节点,接下来如何操作?这里我举一个例子(数字代表值):

怎样让这棵树的答案最小?模拟发现,我们应该先让1变成2,然后把这两个2变成4,最后全部变成7.

发现就是每次操作第二小的值,一直操作到最小值等于它。设auu一开始选的值,fuu这棵子树最终的值,vu的子节点。那么fu=au+fvau
那么我们也可以假定1节点为根,然后进行树形dp。叶子节点的取值肯定是lu,我们尽可能取小的值。然后就对于每个非叶子节点,一种就是我上面举得例子,au最多取到ru,但有些子节点最终的值可能大于ru,那么就要按上述方法操作。但是我们这个操作会操作到非u这棵子树的节点,这个到父节点后又如何考虑呢?我们可以用一个全局变量存一下有多少次操作影响到了上面的点。我们令fu=ru,这样和父节点讨论时,本来应该是ru+fvruafa+fvru,后面那个操作数可以看作整体偏移,把它去掉不影响比较大小,也不影响做差。那么这种情况我们可以直接取fu=ru。还有一种情况就是所有子节点的值都不大于ru,那么取其中最大值即可。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> l(n), r(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> l[i] >> r[i];
    }

    std::vector<std::vector<int> > adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    std::vector<int> f(n);
    i64 ans = 0;
    auto dfs = [&](auto self, int u, int fa) -> void {
    	int max = 0;
    	for (auto & v : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		self(self, v, u);
    		max = std::max(max, f[v]);
    	}

    	if (r[u] >= max) {
    		f[u] = std::max(max, l[u]);
    	} else {
    		for (auto & v : adj[u]) {
    			if (v == fa) {
    				continue;
    			}

    			ans += std::max(0, f[v] - r[u]);
    		}

    		f[u] = r[u];
    	}
    };

    dfs(dfs, 0, -1);
    ans += f[0];
    std::cout << ans << "\n";
}

E1. The Game (Easy Version)

题意:两个人在树上玩游戏,每个点有点权。一开始第一个人选一个点i并删去i这棵子树。往后每一轮,这一轮的玩家都要选一个没被删除的点j,并且wj>wi,并删去j这棵子树,不能操作的人赢。问第一个人一开始有没有一个点选了是必胜的,任意给一个方案。

//我也不知道我的做法会不会被hack,感觉可能tle。不过最终还是过了
我们发现,好像可以选次大值,这样另一个人只能选最大值,那么我们就赢了。但这样是错的,因为可以所有大于我们选的这个点点权的点都在这个点的子树下,那么就会被我们一起删了,导致下一个人无点可选。但这启发我们,如果存在一个点权为i的点,对于任意一个点权大于它的点都会导致删除它会连带删除所有更大的点,而i这个点不会删除所有比它更大的点。那么我们选这个点就行,因为我们选了这个点之后,另一个人只能选更大的点,但这会导致他把所有比这个点更大的点都删掉,于是我们就赢了。权值从大到小枚举所有点,看有没有一个点满足条件。极端情况是一条链的情况,我们每次遍历子树都存有多少个点比当前点大,如果数量正好等于子树大小就标记这个子树,后面访问到就直接返回子树大小就行,因为我们是从大到小枚举的,所以大于前面的点也大于后面的点。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    std::vector<std::vector<int> > w(n + 1);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    	w[a[i]].push_back(i);
    }

    std::vector<std::vector<int> > adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    std::vector<int> b;
    for (int i = n; i >= 1; -- i) {
    	if (!w[i].empty()) {
    		b.push_back(i);
    	}
    }

    std::vector<int> d(n), size(n);
    auto dfs = [&](auto self, int u, int fa) -> void {
    	size[u] = 1;
    	for (auto & v : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		d[v] = d[u] + 1;
    		self(self, v, u);
    		size[u] += size[v];
    	}
    };

    dfs(dfs, 0, -1);

    std::vector<int> st(n);
    int W;
    auto dfs1 = [&](auto self, int u) -> int {
    	if (st[u]) {
    		return size[u];
    	}

    	int sum = a[u] > W;
    	for (auto & v : adj[u]) {
    		if (d[v] != d[u] + 1) {
    			continue;
    		}

    		sum += self(self, v);
    	}

    	if (sum == size[u]) {
    		st[u] = 1;
    	}

    	return sum;
    };

    int sum = w[b[0]].size();
    for (int i = 1; i < b.size(); ++ i) {
    	W = b[i];
    	std::sort(w[b[i]].begin(), w[b[i]].end(), [&](int i, int j) {
    		return d[i] > d[j];
    	});

    	for (auto & x : w[b[i]]) {
    		if (dfs1(dfs1, x) != sum) {
	    		std::cout << x + 1 << "\n";
	    		return;
	    	}
    	} 

    	sum += w[b[i]].size();
    }

    std::cout << 0 << "\n";
}
posted @   maburb  阅读(343)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示