2021/01/22补题(1200+1700)

Belted Rooms (CodeForces - 1428B)

题意:给定n条边连接n个点形成一个环,有单向边和双向边,求有多少个点是可达的(即从该点出发回最终能回到该点)

思路:考虑如果所有边都是方向相同的单向边(或用双向边代替)那么n个点都是可达的,否则如果出现两条方向不同的单向边则只需考虑双向边的条数,所有连接了双向边的点就是可达的点,统计即可

代码

#include <iostream>
#include <cstring>

using namespace std; 

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		bool flag = true;
		int n, ans = 0;
		string s;
		cin >> n >> s;
		char c;
		for (int i = 0; i < n; i++) 
			if (s[i] != '-') {
				c = s[i];
				break;
			}
		for (int i = 0; i < n; i++) {
			if (s[i] == '-') {
				ans += 2;
				if (i > 0 && s[i - 1] == '-')
					ans--;
			}
			if (s[i] != c && s[i] != '-')
				flag = false;
		}
		if (s[0] == '-' && s[n - 1] == '-')
			ans--;
		if (flag)
			printf("%d\n", n);
		else
			printf("%d\n", ans);
				
	}
	return 0;
} 

Program (CodeForces - 1473D)

题意:给出一串由 ‘+’ 与 ‘-’ 构成的字符串,初始值为0,‘+’代表+1,‘-’代表-1,求去掉中间任意一段(l~r)后,剩下的前缀与后缀组合可产生多少不同的数

思路:每次+1与-1生成的数都是相隔为1的连续整数,只需求出前缀与后缀组合后所得数据的极差,但因为是多组数据查询,暴力会超时,所以先预处理一遍前缀与后缀,利用前缀和求解即可 (参考https://blog.csdn.net/tomjobs/article/details/112723795)

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 2e5 + 5;

char s[N];
int pre[N], pre_min[N], pre_max[N];
int suf[N], suf_min[N], suf_max[N];

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n, m, ans = 1;
		scanf("%d%d", &n, &m);
		scanf("%s", s + 1);
		for (int i = 1; i <= n; i++) {
			int k = (s[i] == '+' ? 1 : -1);
			pre[i] = pre[i - 1] + k;
			//求以i为终点的最小前缀和
			pre_min[i] = min(pre_min[i - 1], pre[i]);
			//求以i为终点的最大前缀和 
			pre_max[i] = max(pre_max[i - 1], pre[i]);
		}
		for (int i = 1; i <= n; i++) 
        	printf("%d %d %d\n", pre[i], pre_min[i], pre_max[i]);
        system("pause");
		suf[n + 1] = suf_min[n + 1] = suf_max[n + 1] = 0;
		for (int i = n; i >= 1; i--) {
			int k = (s[i] == '+' ? 1 : -1);
			suf[i] = suf[i + 1] + k;
			//求以i为起点的最小前缀和 
			suf_min[i] = min(0, suf_min[i + 1] + k);
			//求以i为起点的最大前缀和
			suf_max[i] = max(0, suf_max[i + 1] + k);
		}
		while (m--) {
			int l, r;
			scanf("%d%d", &l, &r);
			//记录以l-1为终点的前缀部分可达到的范围 
			int maxn = max(0, pre_max[l - 1]);
			int minn = min(0, pre_min[l - 1]); 
			//后缀在前缀的基础上进行加减 
			int k = pre[l - 1];
			//记录以r+1的起点的前缀部分可达到的范围 
			maxn = max(maxn, suf_max[r + 1] + k);
			minn = min(minn, suf_min[r + 1] + k);
			//答案为去掉中间段后前缀与后缀的极差	
			printf("%d\n", maxn - minn + 1);
		}
	}
	return 0; 
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页