A : 巨石迷阵
问题描述
听说这片土地埋藏着什么秘密,来到这片土地的人不计其数,有人说这里财宝无数,也有人说这里是上古文明留下来的遗迹。小 L 收集情报和资料很久了,只身一人历经千辛万苦终于来到了这片地域的中心地带。突然,四周升起许多巨石,不出所料,面前的正是巨石迷阵。
你面前有 n 块巨石排成一行,每个上面有一个大写字母。
接下来有 m 个询问,每一个询问包含两个数字 l,r ,对于每个询问,你需要回答这个处于区间 [l,r] 的石块上的字母是否每一个英文字母都至少出现了一次。
输入格式
第一行一个整数 n , n≤5×105
第二行,一个长度为 n 的字符串
第三行,一个整数 m, m≤5×105
接下来的 m 行,每行两个整数表示 l,r,1≤l≤r≤n
输出格式
输出包含 m 行,每行一个 YES ,或者 NO 。
分别表示是否每个字母都至少出现一次。
样例输入
30
AAABCDEFGHIJKLMNOPQRSTUVWXYZAA
5
1 26
2 27
3 28
4 29
5 30
样例输出
NO
NO
YES
YES
NO
解答
#include <bits/stdc++.h>
using namespace std;
int b[500001][26];
int main()
{
int n;
cin >> n;
char a[n];
for (int i = 0; i < n; i++)
{
cin >> a[i];
if (i == 0)
{
b[i + 1][a[i] - 'A']++;
}
else
{
for (int j = 0; j < 26; j++)
{
b[i + 1][j] = b[i][j];
if (j == a[i] - 'A')
{
b[i + 1][j]++;
}
}
}
}
int m;
cin >> m;
for (int i = 1; i <= m; i++)
{
int l, r;
cin >> l >> r;
int j = 0;
for (; j < 26; j++)
{
int x = b[r][j] - b[l - 1][j];
if (x <= 0)
{
cout << "NO" << endl;
break;
}
}
if (j >= 26)
{
cout << "YES" << endl;
}
}
return 0;
}
B : 有惊无险
问题描述
解决了巨石迷阵,小 L 长舒一口气。他坐在一棵繁茂的树下刚打开地图,突然,四周轰隆隆又一阵巨响,面前又出现了许多巨石。
情报有误!情报有误!!
根据搜集来的情报,这里不应该再次出现这么多巨石!
小 L 赶忙起身,屏气凝神,重新专注起来...
有一个长度为 n 的字符串,找到一个区间 [l,r],使得处于区间 [l,r] 的石块上的字母,26 个大写字母都至少出现一次。输出这个区间长度的最小值。
数据保证有解
输入格式
第一行一个整数 n , n≤2×105
第二行,一个长度为 n 的字符串
输出格式
一行,一个数,代表最短长度
样例输入
30
AABBCDEFGHIJKLMNOPQRSTUVWXYZZZ
样例输出
27
解答
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
char a[n];
int g = n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
int b[26] = {0};
int l = 0, r = 25;
for (int i = 0; i < 26; i++)
{
int y = a[i] - 'A';
b[y]++;
}
while (1)
{
if (r - l < 25)
{
// if (r == n - 1)
// {
// break;
// }
r++;
b[a[r] - 'A']++;
}
else
{
int j = 0;
for (; j < 26; j++)
{
if (b[j] <= 0)
{
break;
}
}
if (j == 26)
{
if (g > (r - l + 1))
{
g = r - l + 1;
}
b[a[l] - 'A']--;
l++;
}
else
{
r++;
if (r > n - 1)
{
r = n - 1;
b[a[l] - 'A']--;
l++;
}
else
b[a[r] - 'A']++;
}
}
if (r == n - 1 && (r - l) < 25)
{
break;
}
}
cout << g << endl;
return 0;
}
C : 天降甘霖
问题描述
不知道又走了几天,眼前是一片整齐的青石地砖。不管是谁看上一眼,就知道这绝对不是自然生成的。当小 L 踏上青石地板的一瞬间,原本晴朗的天空迅速暗了下来。紧接着乌云密布,下起了雨。雨很快就停了,紧接着天空瞬间放晴,开始升温。潮湿的青石板被很快晒干。
“这是夏天了吗,雨来得快去得也快啊。”长时间孤身一人,小 L 经常自言自语。
但敏锐的 小 L 发现,有一些青石板上的痕迹没有被完全晒干,雨痕竟然拼成了数字。
有 一行 n 个数。输出每 k 个相邻数字的最大值和最小值。
输入格式
第一行两个整数 n,k, 1≤k≤n≤106
第二行,有 n 个数字,中间用空格隔开,每一个数为大小不超过 109 的正整数。
输出格式
有两行,每行 n−k+1 个数字,第一行表示最小值,第二行表示最大值。
样例输入
8 3
1 3 -1 -3 5 3 6 7
样例输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7
解答
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, k;
cin >> n >> k;
pair<int, long long> a[n + 1];
long long min[n] = {0};
int s = 0;
for (int i = 1; i <= n; i++)
{
a[i].first = i;
long long g = 0;
cin >> g;
a[i].second = g;
}
deque<pair<int, long long>> q;
for (int i = 1; i <= k; i++)
{
while (1)
{
if (q.empty() || q.back().second < a[i].second)
{
q.push_back(a[i]);
break;
}
else
{
q.pop_back();
}
}
}
min[s] = q.front().second;
s++;
for (int i = k + 1; i <= n; i++)
{
while (1)
{
if (q.empty() || q.back().second < a[i].second)
{
q.push_back(a[i]);
break;
}
else
{
q.pop_back();
}
}
while (1)
{
int c = (q.front()).first;
if (c < i - k + 1)
{
q.pop_front();
}
else
{
break;
}
}
min[s] = q.front().second;
s++;
}
for (int i = 0; i < s; i++)
{
cout << min[i] << " ";
}
cout << endl;
long long max[n] = {0};
int ss = 0;
// deque<pair<int, int>> q1;
q.clear();
for (int i = 1; i <= k; i++)
{
while (1)
{
if (q.empty() || q.back().second > a[i].second)
{
q.push_back(a[i]);
break;
}
else
{
q.pop_back();
}
}
}
max[ss] = q.front().second;
ss++;
for (int i = k + 1; i <= n; i++)
{
while (1)
{
if (q.empty() || q.back().second > a[i].second)
{
q.push_back(a[i]);
break;
}
else
{
q.pop_back();
}
}
while (1)
{
int c = (q.front()).first;
if (c < i - k + 1)
{
q.pop_front();
}
else
{
break;
}
}
max[ss] = q.front().second;
ss++;
}
for (int i = 0; i < ss; i++)
{
cout << max[i] << " ";
}
cout << endl;
return 0;
}
D : 终而复始
问题描述
青石板路的尽头堆满了财宝。小 L 感到很一阵阵失望,只能先搬走一部分财宝了。
财宝是一个个矩形紧紧挨在一起,第 i 个矩形宽度为 1 ,高度是 hi
小 L 是一个 不会贪心 不贪心的人,所以决定只拿走最大矩形的面积这么多。
拿着拿着,小 L 突然想到,其实这个财宝墙后面还是有路的。
输入格式
第一行一个整数 n , n≤105
第二行,一行数,第 i 个数代表 hi , 0≤hi≤109
输出格式
一行,一个数,代表最大矩形面积
样例输入
7
2 1 4 5 1 3 3
样例输出
8
解答
#include <bits/stdc++.h>
using namespace std;
int n;
stack<pair<int, long long>> s;
long long a[1000100], L[1000010], R[1000010];
// 寻找左边第一个小于该值的数的索引
void Find(long long Ans[])
{
while (!s.empty())
{ // 清空栈
s.pop();
}
for (int i = 1; i <= n; i++)
{
while (!s.empty() && s.top().second >= a[i])
{
s.pop();
}
if (s.empty())
{
Ans[i] = i;
}
else
{
Ans[i] = i - s.top().first;
}
s.push(pair<int, long long>(i, a[i]));
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
long long Ans = 0;
Find(L);
reverse(a + 1, a + n + 1);
Find(R);
reverse(a + 1, a + n + 1);
reverse(R + 1, R + n + 1);
for (int i = 1; i <= n; i++)
{
Ans = max(Ans, a[i] * (L[i] + R[i] - 1));
}
cout << Ans << endl;
return 0;
}
E : 旅途不止
问题描述
小 L 至今没有回来。这太正常,就像其他没有回来的探险者一样。没有人会提起小 L ,也没有人述说小 L 的故事。
多年以后,你也踏上了这片神秘的土地,眼前出现了一道谜题。
有一列长度为 n 的数,初始值都是 1 。
有 m 次操作,每次对属于区间 [l,r] 的数都乘上一个数 cb ,最后输出这 n 个数的最大公约数。
谜题面前有一张地图,上面署名 "小 L"。
输入格式
第一行一个整数 n , n≤105
第二行,一个整数 m, m≤105 接下来的 m 行,每行四个整数表示 l,r,c,b 1≤l≤r≤n,1≤c≤100,0≤b≤109
输出格式
一行,一个数,代表最大公约数,答案对 109+7 取模。
样例输入
5
3
1 4 3 2
2 4 2 2
3 5 6 1
样例输出
3
初始:[1,1,1,1,1]
第一次操作后,[9,9,9,9,1]
第二次操作后,[9,36,36,36,1]
第三次操作后,[9,36,216,216,6] gcd(9,36,216,216,6)=3,gcd 表示最大公约数
地图上画的正是当年小 L 探索过的区域,但小 L 去了哪里还是不得而知。
地图的背面,写着如下内容:
n 个数的最大公约数求法:
设第 i 个数为 ai,则 ai=∏jpjbij,其中 pj 表示第 j 个素数,bij 表示第 i 个数质因数分解之后 pj 的幂次。
则 gcd(a1,...,an)=∏jpjminbij。
举例求 gcd(60,24,36,42) 60=22×31×51×70 24=23×31×50×70 36=22×32×50×70
42=21×31×50×71
所以
gcd(60,24,36,42)=2min(2,3,2,1)×3min(1,1,2,1)×5min(1,0,0,0)×3min(0,0,0,0)=21×31×50×70=6
解答
#include <bits/stdc++.h>
using namespace std;
long long dd[100001][100];
// 质数判断
bool prime(long long a)
{
if (a == 1)
{
return false;
}
else if (a == 2)
{
return true;
}
for (long long i = 2; i * i <= a; i++)
{
if (a % i == 0)
{
return false;
}
else
{
continue;
}
}
return true;
}
// 快速幂计算并取模
long long P(long long x, long long n, long long mod)
{
long long r = 1;
while (n)
{
if (n & 1)
{
r = (r * x) % mod;
}
x = (x * x) % mod;
n = n >> 1;
}
return r;
}
int main()
{
long long n, m, max = 0;
cin >> n >> m;
long long res = 1;
// 初始化
for (long long i = 0; i < n + 1; i++)
{
for (long long j = 0; j < 100; j++)
{
dd[i][j] = 0;
}
}
// m次操作
for (long long i = 1; i <= m; i++)
{
long long l, r, c, b;
cin >> l >> r >> c >> b;
if (prime(c))
{ // c为质数
dd[l - 1][c] += b; // 使用差分进行记录多次操作之后次方数
dd[r][c] -= b;
if (max < c) // 寻找最大的质因数,用于之后的寻找最大质因数
{
max = c;
}
}
else
{ // c不为质数,将c转化为多个质数
for (long long p = 2; p <= c; p++)
{
if (prime(p))
{
while (c % p == 0)
{
dd[l - 1][p] += b;
dd[r][p] -= b;
c = c / p;
if (max < p)
{
max = p;
}
}
}
}
}
}
for (long long i = 2; i <= max; i++)
{
long long k = 0;
long long min = dd[0][i];
for (long long j = 0; j < n; j++)
{
k = k + dd[j][i];
if (min > k)
{
min = k;
}
}
res = res * P(i, min, 1000000007) % 1000000007;
}
cout << res << endl;
return 0;
}