第五周 简单数学与线性结构

A 巨石迷阵

问题描述
听说这片土地埋藏着什么秘密,来到这片土地的人不计其数,有人说这里财宝无数,也有人说这里是上古文明留下来的遗迹。小 L 收集情报和资料很久了,只身一人历经千辛万苦终于来到了这片地域的中心地带。突然,四周升起许多巨石,不出所料,面前的正是巨石迷阵。

你面前有 nn 块巨石排成一行,每个上面有一个大写字母。
接下来有 mm 个询问,每一个询问包含两个数字 l,rl,r ,对于每个询问,你需要回答这个处于区间 [l,r][l,r] 的石块上的字母是否每一个英文字母都至少出现了一次。

输入格式
第一行一个整数 nn , n\le5\times10^5n≤5×10
5

第二行,一个长度为 nn 的字符串
第三行,一个整数 mm, m\le5\times10^5m≤5×10
5

接下来的 mm 行,每行两个整数表示 l,r,1\le l\le r\le nl,r,1≤l≤r≤n

输出格式
输出包含 mm 行,每行一个 \text{YES}YES ,或者 \text{NO}NO 。
分别表示是否每个字母都至少出现一次。

样例输入
30
AAABCDEFGHIJKLMNOPQRSTUVWXYZAA
5
1 26
2 27
3 28
4 29
5 30
样例输出
NO
NO
YES
YES
NO

#include <iostream>

int toint(char c)
{
    int n = c - 'A';
    return n;
}

int main()
{
    int n;
    int **sum;
    int m;
    scanf("%d\n", &n);
    sum = new int*[n + 1];
    for (int i = 0; i < n + 1; i++)
    {
        sum[i] = new int[26];
        for (int j = 0; j < 26; j++)
        {
            sum[i][j] = 0;
        }
    }
    for (int i = 1; i < n + 1; i++)
    {
        char c;
        int ix;
        scanf("%c", &c);
        ix = toint(c);
        for (int j = 0; j < 26; j++)
            sum[i][j] = sum[i - 1][j];    
        sum[i][ix] = sum[i - 1][ix] + 1;
    } 

    scanf("%d", &m);
    while (m > 0)
    {
        int l, r;
        bool yes = true;
        scanf("%d %d", &l, &r);
        for (int i = 0; i < 26; i++)
        {
            if (sum[r][i] - sum[l - 1][i] <= 0)
            {
                yes = false;
                break;
            }
        }
        printf("%s\n", yes ? "YES" : "NO");

        m--;
    }

    return 0;
}

在这里插入图片描述

B : 有惊无险

问题描述
解决了巨石迷阵,小 L 长舒一口气。他坐在一棵繁茂的树下刚打开地图,突然,四周轰隆隆又一阵巨响,面前又出现了许多巨石。

情报有误!情报有误!!

根据搜集来的情报,这里不应该再次出现这么多巨石!
小 L 赶忙起身,屏气凝神,重新专注起来…

有一个长度为 nn 的字符串,找到一个区间 [l,r][l,r],使得处于区间 [l,r][l,r] 的石块上的字母,26个大写字母都至少出现一次。输出这个区间长度的最小值。

数据保证有解

输入格式
第一行一个整数 nn , n\le2\times10^5n≤2×10
5

第二行,一个长度为 nn 的字符串

输出格式
一行,一个数,代表最短长度

样例输入
30
AABBCDEFGHIJKLMNOPQRSTUVWXYZZZ
样例输出
27

#include <iostream>

int toint(char c)
{
    int n = c - 'A';
    return n;
}

bool find(int **sum, int len, int start, int n)
{
    if (start <= len)
    {
        return true;
    }
    for (int i = start; i < n + 1; i++)
    {
        if (i - len + 1 < 0)
        {
            break;
        }
        for (int j = i - len + 1; j <= i; j++)
        {
            bool yes = true;
            for (int x = 0; x < 26; x++)
            {
                if (sum[i][x] - sum[j - 1][x] <= 0)
                {
                    yes = false;
                    break;
                }
            }
            if (yes)
            {
                return true;
            }
        }
    }
    return false;
}

int main()
{
    int n;
    int **sum;
    scanf("%d\n", &n);
    sum = new int*[n + 1];
    for (int i = 0; i < n + 1; i++)
    {
        sum[i] = new int[26];
        for (int j = 0; j < 26; j++)
        {
            sum[i][j] = 0;
        }
    }
    for (int i = 1; i < n + 1; i++)
    {
        char c;
        int ix;
        scanf("%c", &c);
        ix = toint(c);
        for (int j = 0; j < 26; j++)
            sum[i][j] = sum[i - 1][j];    
        sum[i][ix] = sum[i - 1][ix] + 1;
    } 
    
    
    int start = 0;
    for (int i = 1; i < n + 1; i++)
    {
        bool all = true;
        for (int x = 0; x < 26; x++)
        {
            if (sum[i][x] == 0)
            {
                all = false;
                break;
            }
        }
        if (all)
        {
            start = i;
            break;
        }
    }
    
    // printf("%d ))\n", start);
    // return 0;
    
    int l = 1, r = n * 2;
    int mid;
    while (true)
    {
        mid = (l + r) / 2;
        // printf("%d %d %d\n", l, mid, r);
        if (l == mid) break;
        if (find(sum, mid, start, n))
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
        
        
    }
    
    printf("%d\n", r);
    
    return 0;
}

在这里插入图片描述

C : 天降甘霖

问题描述
不知道又走了几天,眼前是一片整齐的青石地砖。不管是谁看上一眼,就知道这绝对不是自然生成的。当小 L 踏上青石地板的一瞬间,原本晴朗的天空迅速暗了下来。紧接着乌云密布,下起了雨。雨很快就停了,紧接着天空瞬间放晴,开始升温。潮湿的青石板被很快晒干。

“这是夏天了吗,雨来得快去得也快啊。”长时间孤身一人,小 L 经常自言自语。

但敏锐的 小 L 发现,有一些青石板上的痕迹没有被完全晒干,雨痕竟然拼成了数字。

有 一行 nn 个数。输出每 kk 个相邻数字的最大值和最小值。

输入格式
第一行两个整数 n,kn,k, 1\le k\le n\le10^61≤k≤n≤10
6

第二行,有 nn 个数字,中间用空格隔开,每一个数为大小不超过 10^910
9
的正整数。

输出格式
有两行,每行 n-k+1n−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<iostream>
#include<cstdio>
using namespace std;
int n,k;

int Min[1000010],Max[1000010],zjm[1000010],queue[1000010];
int Minc()
{	int l=0,r=-1;
	
	for(int i=0;i<n;i++)
	{
		while (l<=r&&(i-queue[l])>=k)//
		l++;
	while(l<=r&&zjm[i]<zjm[queue[r]])
		r--;

	queue[++r]=i;
	
	Min[i]=zjm[queue[l]];
	}
	for(int i=k-1;i<n;i++)//第k-1才开始有效 
	cout<<Min[i]<<' ';

}

int Maxc()
{	int l=0,r=-1;
	
	for(int i=0;i<n;i++)
	{
	while(l<=r&&(i-queue[l])>=k)//不属于当前区间
		l++; 
	while(l<=r&&zjm[i]>zjm[queue[r]])
		r--;
	queue[++r]=i;
	Max[i]=zjm[queue[l]];
	
	}
	for(int i=k-1;i<n;i++)
		cout<<Max[i]<<' ';



}


int main()
{	
	scanf("%d %d",&n,&k);
	
	for(int i=0;i<n;i++)
		scanf("%d",&zjm[i]);
	
	Minc();
	cout<<endl;
	Maxc();


}

在这里插入图片描述

D : 终而复始

问题描述
青石板路的尽头堆满了财宝。小 L 感到很一阵阵失望,只能先搬走一部分财宝了。

财宝是一个个矩形紧紧挨在一起,第 ii 个矩形宽度为 11 ,高度是 h_ih
i

小 L 是一个 不会贪心 不贪心的人,所以决定只拿走最大矩形的面积这么多。

拿着拿着,小 L 突然想到,其实这个财宝墙后面还是有路的。

输入格式
第一行一个整数 nn , n\le 10^5n≤10
5

第二行,一行数,第 ii 个数代表 h_ih
i

, 0\le h_i\le 10^90≤h
i

≤10
9

输出格式
一行,一个数,代表最大矩形面积

样例输入
7
2 1 4 5 1 3 3
样例输出
8
在这里插入图片描述

#include<iostream>
using namespace std;

	long long Left[100100],Right[100100];
	long long a[100100];
	long long stack[100100];
	long long n;
	
int leftsize()
	{	
		long long l=-1;
		for(int i=0;i<n;i++)
			{
			while(l>=0&&a[i]<=a[stack[l]])//新的数小于栈顶元素 
					l--;//弹出去 ,剩左边最后一个比它小的元素 
				if(l==-1)
				Left[i]=0;//左边没有比它大的元素了 
				else
				Left[i]=stack[l]+1;
				stack[++l]=i;//入栈 
			
			
			}
				
	} 	
	
	int rightsize()
	{	
		long long r=-1;
		for(int i=n-1;i>=0;i--)
			{
			while(r>=0&&a[i]<=a[stack[r]])//新的数小于栈顶元素 
					r--;
				if(r==-1)
				Right[i]=n-1;
				else
				Right[i]=stack[r]-1;
				stack[++r]=i;//入栈 
			}
				
	} 

int main()
{	long long max;
	
	while(cin>>n)
{	
	if(n==0)
	break;
	max=0;
		for(int i=0;i<n;i++)
			cin>>a[i];
		leftsize();
		rightsize();
		for(int i=0;i<n;i++)
		{
			long long p=a[i]*(Right[i]-Left[i]+1);
			if(max<p)
				max=p;
		
		}


	cout<<max<<endl;


}
}

在这里插入图片描述

E : 旅途不止

问题描述
小 L 至今没有回来。这太正常,就像其他没有回来的探险者一样。没有人会提起小 L ,也没有人述说小 L 的故事。
多年以后,你也踏上了这片神秘的土地,眼前出现了一道谜题。

有一列长度为 nn 的数,初始值都是 11 。
有 mm 次操作,每次对属于区间 [l,r][l,r] 的数都乘上一个数 c^bc
b
,最后输出这 nn 个数的最大公约数。

谜题面前有一张地图,上面署名 “小 L”。

输入格式
第一行一个整数 nn , n\le 10^5n≤10
5

第二行,一个整数 mm, m \le 10^5m≤10
5

接下来的 mm 行,每行四个整数表示 l,r,c,bl,r,c,b
1\le l\le r\le n,1\le c\le 100,0\le b\le 10^91≤l≤r≤n,1≤c≤100,0≤b≤10
9

输出格式
一行,一个数,代表最大公约数,答案对 10^9+710
9
+7 取模。

样例输入
5
3
1 4 3 2
2 4 2 2
3 5 6 1
样例输出
3
初始:[1,1,1,1,1][1,1,1,1,1]
第一次操作后,[9,9,9,9,1][9,9,9,9,1]
第二次操作后,[9,36,36,36,1][9,36,36,36,1]
第三次操作后,[9,36,216,216,6][9,36,216,216,6]

\gcd(9,36,216,216,6)=3gcd(9,36,216,216,6)=3,gcd表示最大公约数

地图上画的正是当年小 L 探索过的区域,但小 L 去了哪里还是不得而知。
地图的背面,写着如下内容:

nn 个数的最大公约数求法:

设第 ii 个数为 a_ia
i

,则 a_i=\prod_j p_j^{b_{ij}}a
i

=∏
j

p
j
b
ij


,其中 p_jp
j

表示第 jj 个素数,b_{ij}b
ij

表示第 ii 个数质因数分解之后 p_jp
j

的幂次。
则 gcd(a_1,…,a_n)=\prod_j p_j^{\min{b_{ij}}}gcd(a
1

,…,a
n

)=∏
j

p
j
minb
ij


举例求 gcd(60,24,36,42)gcd(60,24,36,42)

60=22\times31\times51\times7060=2
2
×3
1
×5
1
×7
0

24=23\times31\times50\times7024=2
3
×3
1
×5
0
×7
0

36=22\times32\times50\times7036=2
2
×3
2
×5
0
×7
0

42=21\times31\times50\times7142=2
1
×3
1
×5
0
×7
1

所以

\begin{aligned}gcd(60,24,36,42)&=2{\min(2,3,2,1)}\times3{\min(1,1,2,1)}\times5{\min(1,0,0,0)}\times3{\min(0,0,0,0)}\&=21\times31\times50\times70\&=6 \end{aligned}
gcd(60,24,36,42)

=2
min(2,3,2,1)
×3
min(1,1,2,1)
×5
min(1,0,0,0)
×3
min(0,0,0,0)

=2
1
×3
1
×5
0
×7
0

=6

#include <iostream>

bool prime(long long a)
{
    if (a == 1) return false;
    if (a == 2) return true;
    for (long long i = 2; i * i <= a; i++)
        if (a % i == 0)  return false;
    return true;
}

long long power(long long x, long long n, long long mod)
{
    long long res = 1;
    while (n)
    {
        if (n & 1)
            res = (res * x) % mod;
        x = (x * x) % mod;
        n >>= 1;
    }
    return res;
}


int main(int argc, char **argv)
{
    long long n, m, maxp = 0;
    long long **parts;
    long long res = 1;
    scanf("%lld %lld", &n, &m);
    
    parts = new long long*[n + 1];
    
    for (long long i = 0; i < n + 1; i++)
    {
        parts[i] = new long long[100];
        for (long long j = 0; j < 100; j++)
        {
            parts[i][j] = 0;
        }
    }
    
    for (long long i = 0; i < m; i++)
    {
        long long l, r, c, b;
        scanf("%lld %lld %lld %lld", &l, &r, &c, &b);
        if (prime(c))
        {
            parts[l - 1][c] += b;
            parts[r][c] -= b;
            maxp = maxp < c ? c : maxp;
        }
        else
        {
            for (long long p = 2; p <= c; p++)
            {
                // std::cout << p << " ";
                if (prime(p))
                {
                    // std::cout << p << "+ ";
                    while(c % p == 0)
                    {
                        parts[l - 1][p] += b;
                        parts[r][p] -= b;
                        c /= p;
                        maxp = maxp < p ? p : maxp;
                    }
                }
            }
            // std::cout << "\n";
        }
    }
    
    
    for (long long i = 2; i <= maxp; i++)
    {
        long long k = 0;
        long long minp = parts[0][i];
        for (long long j = 0; j < n; j++)
        {
            // std::cout << parts[j][i] << " ";
            k += parts[j][i];
            minp = minp > k ? k : minp;
        }
        // std::cout << "\n";
        res = res * power(i, minp, 1000000007) % 1000000007;
    }
    
    printf("%lld\n", res);
    
    
    return 0;
}

在这里插入图片描述

解题思路

在这里插入图片描述

如何求矩形最大面积(和D题基本一样)

在这里插入图片描述

在这里插入图片描述

二维前缀和

在这里插入图片描述
前俩或者前三个都可以暴力求解,不过第二个挺麻烦的,需要注意if和for循环嵌套不要混乱。
第三个和去年的一道题一样,相当于把队列queue整体一次往后移动一个(先判断是不是要移动),然后在这个新的区间里面求最大值最小值。新的元素放到队尾,先进先出。

最大公约数

最后一个几乎不会,只会求两个数最大公约数,还是在体系结构实验里的汇编代码学的,题里给了个这个:
在这里插入图片描述
这例子好像都给错了,但学到了思路:先把n个数全都用质数的因数表示出来,最大公约数就是质数的最低次幂乘在一起的结果。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值