今天是初四,已经休息三天了,今天开始继续学习,然后觉得确实玩久了不太适应,已经有惰性了,不过还好自己喜欢,还是慢慢的坚持了下来,本来今天要看理论课的,也没看成,玩手机没停下来,不过好在做了些题,慢慢来吧。
标签:数学
思路:
自己只能想出个暴力过了一半的数据,然后标准答案见示例代码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
#include
#include
#include
#include
#include
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
#include
#include
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
#include
#include
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
示例代码:
#include
#include
#include
using namespace std;
const int N = 1e5+10;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
int 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
#include
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;
}