目录
- 引言
- 一、FEB
- 二、区间合并
- 三、管道
- 四、填充
引言
今天是初四,已经休息三天了,今天开始继续学习,然后觉得确实玩久了不太适应,已经有惰性了,不过还好自己喜欢,还是慢慢的坚持了下来,本来今天要看理论课的,也没看成,玩手机没停下来,不过好在做了些题,慢慢来吧。
一、FEB
标签:数学
思路:
自己只能想出个暴力过了一半的数据,然后标准答案见示例代码2
题目描述:
有一个长度为 N 的字符串 S,其中的每个字符要么是 B,要么是 E。我们规定 S 的价值等于其中包含的子串 BB 以及子串 EE 的数量之和。例如,BBBEEE 中包含 2 个 BB 以及 2 个 EE,所以 BBBEEE 的价值等于 4。我们想要计算 S 的价值,不幸的是,在我们得到 S 之前,约翰将其中的一些字符改为了 F。目前,我们只能看到改动后的字符串 S,对于其中的每个 F,我们并不清楚它之前是 B 还是 E。请你计算,改动前的 S 有多少种可能的价值并将所有可能价值全部输出。输入格式
第一行包含一个整数 N。第二行包含改动后的字符串 S。输出格式
第一行输出一个整数 K,表示改动前的 S 的可能价值的数量。接下来 K 行,按照升序顺序,每行输出一个可能价值。数据范围
1≤N≤2×105
输入样例1:
4
BEEF
输出样例1:
2
1
2
输入样例2:
9
FEBFEBFEB
输出样例2:
2
2
3
输入样例3:
10
BFFFFFEBFE
输出样例3:
3
2
4
6
示例代码1:DFS
#include <cstdio>
#include <string>
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>using namespace std;string str;
unordered_map<int,int> map;
vector<int> res;int get()
{int res = 0;for(int i = 1; i < str.size(); ++i){if(str[i] == str[i-1]){res++;}}return res;
}void dfs(int u)
{if(u == str.size()){//cout << str << endl;int t = get();if(!map.count(t)){map[t]++;res.push_back(t);}return;}if(str[u] != 'F'){dfs(u+1);return;}str[u] = 'B';dfs(u+1);str[u] = 'E';dfs(u+1);str[u] = 'F'; // 这里一定要加上不然后面遍历后,再从前面开始这块就不是'F'了
}int main()
{ios::sync_with_stdio(false);cin.tie(0);int n;cin >> n;cin >> str;dfs(0);sort(res.begin(), res.end());cout << res.size() << endl;for(int x: res){cout << x << endl;}return 0;
}
示例代码2:
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;int n;
string s;int main()
{cin >> n >> s;if(s == string(n, 'F')){cout << n << endl;for(int i = 0; i < n; ++i)cout << i << endl;}else{int l = 0, r = n - 1;while(s[l] == 'F') l++;while(s[r] == 'F') r--;int low = 0, high = 0;auto str = s;for(int i = l; i <= r; ++i){if(str[i] == 'F'){if(str[i-1] == 'B') str[i] = 'E';else str[i] = 'B';}if(i > l && str[i] == str[i-1]) low++;}str = s;for(int i = l; i <= r; ++i){if(str[i] == 'F') str[i] = str[i-1];if(i > l && str[i] == str[i-1]) high++;}int ends = l + n - 1 - r, d = 2;if(ends) high += ends, d = 1;cout << (high - low) / d + 1 << endl;for(int i = low; i <= high; i += d)cout << i << endl;}return 0;
}
二、区间合并
标签:区间合并
思路:
模板题没什么说的,马上就用了,熟悉一下
题目描述:
给定 n 个区间 [li,ri],要求合并所有有交集的区间。注意如果在端点处相交,也算有交集。输出合并完成后的区间个数。例如:[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。输入格式
第一行包含整数 n。接下来 n 行,每行包含两个整数 l 和 r。输出格式
共一行,包含一个整数,表示合并区间完成后的区间个数。数据范围
1≤n≤100000,−109≤li≤ri≤109
输入样例:
5
1 2
2 4
5 6
7 8
7 9
输出样例:
3
示例代码:
#include <cstdio>
#include <iostream>
#include <algorithm>using namespace std;const int N = 1e5+10;struct Len
{int l, r;bool operator<(const Len& other){return l < other.l;}
}len[N];
int n;int main()
{scanf("%d", &n);for(int i = 0; i < n; ++i){scanf("%d%d", &len[i].l, &len[i].r);}sort(len, len+n);int res = 0;int l = -2e9, r = -2e9;for(int i = 0; i < n; ++i){if(len[i].l > r){res++;l = len[i].l;r = len[i].r;}else{r = max(r, len[i].r);}}cout << res << endl;return 0;
}
三、管道
标签:二分、区间合并
思路:
就是给一条长为 m m m 的管子,每个管子中间有一个阀门和水流检测器,给出特定阀门打开的编号和时间,算能够使得所有监视器都检测到水的最短时间。明显这时间越长肯定能通过,所以单调性用二分,然后就是check条件了,假设最短时间为 m i d mid mid ,则会有多个区间代表水能够流通的管道编号,要做的就是把这些区间找出来,然后合并,看是否能够合并成从 [ 1 , m ] [1,m] [1,m] 的一个区间
题目描述:
有一根长度为 len 的横向的管道,该管道按照单位长度分为 len 段,每一段的中央有一个可开关的阀门和一个检测水流的传感器。一开始管道是空的,位于 Li 的阀门会在 Si 时刻打开,并不断让水流入管道。对于位于 Li 的阀门,它流入的水在 Ti(Ti≥Si)时刻会使得从第 Li−(Ti−Si) 段到第 Li+(Ti−Si) 段的传感器检测到水流。求管道中每一段中间的传感器都检测到有水流的最早时间。输入格式
输入的第一行包含两个整数 n,len,用一个空格分隔,分别表示会打开的阀门数和管道长度。接下来 n 行每行包含两个整数 Li,Si,用一个空格分隔,表示位于第 Li 段管道中央的阀门会在 Si 时刻打开。输出格式
输出一行包含一个整数表示答案。数据范围
对于 30% 的评测用例,n≤200,Si,len≤3000;对于 70% 的评测用例,n≤5000,Si,len≤105;
对于所有评测用例,1≤n≤105,1≤Si,len≤109,1≤Li≤len,Li−1<Li。输入样例:
3 10
1 1
6 5
10 2
输出样例:
5
示例代码:
#include <cstdio>
#include <iostream>
#include <algorithm>using namespace std;const int N = 1e5+10;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondint n, m;
PII w[N], q[N];bool check(int mid)
{int cnt = 0;for(int i = 0; i < n; ++i){int L = w[i].x, S = w[i].y;if(mid >= S){int t = mid - S;int l = max(1, L - t), r = min((LL)m, (LL)L + t);q[cnt++] = {l,r};}}sort(q, q+cnt);int l = -1, r = -1;for(int i = 0; i < cnt; ++i){if(q[i].x > r + 1) l = q[i].x, r = q[i].y;else r = max(r, q[i].y);}return l == 1 && r == m;
}int main()
{scanf("%d%d", &n, &m);for(int i = 0; i < n; ++i) scanf("%d%d", &w[i].x, &w[i].y);int l = 0, r = 2e9;while(l < r){int mid = (LL)l + r >> 1;if(check(mid)) r = mid;else l = mid + 1;}printf("%d\n", r);return 0;
}
四、填充
标签:贪心
思路:
这个跟FEB区别在于没有重叠,所以可以用贪心的方式
题目描述:
有一个长度为 n 的 01 串,其中有一些位置标记为 ?,这些位置上可以任意填充 0 或者 1,请问如何填充这些位置使得这个 01 串中
出现互不重叠的 00 和 11 子串最多,输出子串个数。输入格式
输入一行包含一个字符串。输出格式
输出一行包含一个整数表示答案。数据范围
对于所有评测用例,1≤n≤106。输入样例:
1110?0
输出样例:
2
样例解释
如果在问号处填 0,则最多出现一个 00 和一个 11:111000。
示例代码:
#include <cstdio>
#include <iostream>using namespace std;const int N = 1e6+10;string str;int main()
{ios::sync_with_stdio(false); cin.tie(0);cin >> str;int res = 0;for(int i = 1; i < str.size(); ++i){if(str[i] == str[i-1] || str[i] == '?' || str[i-1] == '?'){i++;res++;}}printf("%d\n", res);return 0;
}