山东大学-2024 程序设计思维与实践week 5 巨石迷阵+有惊无险+天降甘霖+终而复始

一、巨石迷阵

问题描述

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

你面前有 , 块巨石排成一行,每个上面有一个大写字母。
接下来有 m 个询问,每一个询问包含两个数字l,r”,对于每个询问,你需要回答这个处于区间,,的石块上的字母是否每一个英文字

母都至少出现了一次。

输入格式

第-行-个整数n,n <5*10^5
第二行,一个长度为 n 的字符串
第三行,一个整数m,m<5*10^5
接下来的 m 行,每行两个整数表示 l,r,l ≤l≤r< n

输出格式

输出包含 m 行,每行一个 YES,或者 NO
分别表示是否每个字母都至少出现一次。

样例输入

30
AAABCDEFGHIJKLMNOPORSTUVWXYZAA
5
1 26
2 27
3 28
4 29
5 30

样例输出

NO
NO
YES
YES
NO

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;cin>>n;
    int **sum=new int*[n+1];
    sum[0]=new int[26];
    for(int i=0;i<26;i++){
        sum[0][i]=0;
    }
    for(int i=1;i<=n;i++){
        sum[i]=new int[26];
        char temp;cin>>temp;
        temp=temp-'A';
        copy(&sum[i-1][0],&sum[i-1][26],sum[i]);
        sum[i][temp]++;
    }
    int m;cin>>m;
    int b,e;
    while(m--){
        cin>>b>>e;
        bool flag=false;
        for(int i=0;i<26;i++){
            if(sum[e][i]-sum[b-1][i]==0){
                flag=true;
                break;
            }
        }
        if(flag){
            cout<<"NO\n";
        }
        else{
            cout<<"YES\n";
        }
    }
    return 0;
}

二、有惊无险

问题描述


解决了巨石迷阵,小乚长舒一口气。他坐在一棵繁茂的树下刚打开地图,突然,四周轰隆隆又一阵巨响,面前又出现了许多巨石。
情报有误!情报有误!!
根据搜集来的情报,这里不应该再次出现这么多巨石!
小L赶忙起身,屏气凝神,重新专注起来...


有一个长度为” 的字符串,找到一个区间,",使得处于区间l, 的石块上的字母,26 个大写字母都至少出现一次。输出这个区间长度的最小值。
数据保证有解

输入格式

第一行-个整数n,n<2x105
第二行,一个长度为 n 的字符串

输出格式

一行,一个数,代表最短长度

样例输入

30
AABBCDEFGHIJKLMNOPORSTUVWXYZZZ


样例输出

27

#include <bits/stdc++.h>
using namespace std;


int main()
{
    int n;cin>>n;
    string s;
    cin>>s;
    vector<int> ans;
    bool b[26];
    int count=0;
    int init=0;
    fill(b,b+26,false);
    for(int i=0;i<n;i++){
        if(b[s[i]-'A']==false){
            count++;
            b[s[i]-'A']=true;
        }
        if(count==26){
            count=0;
            fill(b,b+26,false);
            init=i;
            break;
        }
    }
    ans.push_back(init+1);
    fill(b,b+26,false);
    for(int i=init;i<n;i++){
        for(int j=i;j>=0;j--){
            if(b[s[j]-'A']==false){
                count++;
                b[s[j]-'A']=true;
            }
            if(count==26){
                count=0;
                fill(b,b+26,false);
                ans.push_back(i-j+1);
                break;
            }
        }
    }
    sort(ans.begin(),ans.end());
    cout<<ans[0];

    return 0;
}

三、天降甘霖

问题描述


不知道又走了几天,眼前是一片整齐的青石地砖。不管是谁看上一眼,就知道这绝对不是自然生成的。当小L踏上青石地板的一瞬间,原本晴朗的天空迅速暗了下来。紧接着乌云密布,下起了雨。雨很快就停了,紧接着天空瞬间放晴,开始升温。潮湿的青石板被很快晒这是夏天了吗,雨来得快去得也快啊。”长时间孤身一人,小L经常自言自语但敏锐的 小L发现,有一些青石板上的痕迹没有被完全晒干,雨痕竟然拼成了数字。
有 一行 n 个数。输出每 k个相邻数字的最大值和最小值。

输入格式

第-行两个整数 n,ん,1<k<n< 106
第二行,有 几 个数字,中间用空格隔开,每一个数为绝对值不超过 10”的整数。

输出格式

有两行,每行 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;

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    vector<int> result;
    deque<int> window; // 单调队列,存储元素的下标

    for (int i = 0; i < nums.size(); ++i) {
        // 如果队列不为空,且当前元素大于队尾元素,弹出队尾元素
        while (!window.empty() && nums[i] >= nums[window.back()]) {
            window.pop_back();
        }

        // 将当前元素的下标加入队列
        window.push_back(i);

        // 如果队首元素不在当前窗口范围内,弹出队首元素
        if (i - window.front() >= k) {
            window.pop_front();
        }

        // 当窗口长度达到k时,记录当前窗口的最大值
        if (i >= k - 1) {
            result.push_back(nums[window.front()]);
        }
    }

    return result;
}
vector<int> minSlidingWindow(vector<int>& nums, int k) {
    vector<int> result;
    deque<int> window;

    for (int i = 0; i < nums.size(); ++i) {
        while (!window.empty() && nums[i] <= nums[window.back()]) {
            window.pop_back();
        }

        window.push_back(i);

        if (i - window.front() >= k) {
            window.pop_front();
        }

        if (i >= k - 1) {
            result.push_back(nums[window.front()]);
        }
    }

    return result;
}

int main() {
    int n,k;cin>>n>>k;
    vector<int> nums(n);
    for(int i=0;i<n;i++){
        cin>>nums[i];
    }
    vector<int> result = maxSlidingWindow(nums, k);
    vector<int> result_min= minSlidingWindow(nums,k);
    for (int num : result_min) {
        cout << num << " ";
    }
    cout << endl;

    for (int num : result) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

四、终而复始

问题描述

青石板路的尽头堆满了财宝。小L感到很一阵阵失望,只能先搬走一部分财宝了财宝是一个个矩形紧紧挨在一起,第之个矩形宽度为1,高度是 h;小L是一个不会贪心不贪心的人,所以决定只拿走最大矩形的面积这么多拿着拿着,小L突然想到,其实这个财宝墙后面还是有路的。

输入格式

第一行一个整数n,n < 10^5
第二行,一行数,第i个数代表 h_i,0 ≤ h_i≤ 109

输出格式

一个数,代表最大矩形面积

样例输入

7
2 1 4 5 1 3 3

样例输出

8

#include<bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin>>n;
    vector<long long> v(n);
    for(int i=0;i<n;i++){
        cin>>v[i];
    }
    long long ans=0;
    stack<long long> s;
    long long h=0;
    for(int i=0;i<=n;i++){
        while(!s.empty() && (v[i]<v[s.top()] ||i==n)){
            h=v[s.top()];
            s.pop();
            long long w = s.empty() ? i : i - s.top() - 1;
            ans=max(ans,(long long)w*h);
        }
        s.push(i);
    }

    cout<<ans<<endl;


    return 0;
}

五、旅途不止

问题描述

小L至今没有回来。这太正常,就像其他没有回来的探险者一样。没有人会提起小L,也没有人述说小L的故事。多年以后,你也踏上了这片神秘的土地,眼前出现了一道谜题。
有一列长度为n 的数,初始值都是1。
有 m 次操作,每次对属于区间[l,r]的数都乘上一个数 c^b,最后输出这 几 个数的最大公约数。
谜题面前有一张地图,上面署名"小L"。

输入格式

第-行一个整数 n,n < 10^5
第二行,一个整数 m,m < 10^5接下来的 m 行,每行四个整数表示l,r,c,b 1<l<r<n,1<c<100,0<b< 10^9

输出格式

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

样例输入

5
3
1 4 3 2
2 4 2 2
3 5 6 1


样例输出

3

#include<bits/stdc++.h>
#define fec 1000000007

using namespace std;
const int prime[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,};



vector<unsigned long long> factorization(int n,int b){//求c^b的素数拆分
    vector<unsigned long long> result(25);
    fill(result.begin(),result.end(),0);
    for(int i=0;i<25 && n!=1;i++){
        while(n%prime[i]==0){
            result[i]++;
            n=n/prime[i];
        }
        result[i]=result[i]*b;
    }
    return result;
}
unsigned long long epow(int pri, unsigned long long exp) {//模运算的快速幂
    if (exp == 0)
        return 1 ;
    else if (exp == 1)
        return pri ;

    unsigned long long result;
    result= exp&1 ? (pri * epow(pri, (exp - 1) / 2) % fec * epow(pri, (exp - 1) / 2) % fec) % fec : (epow(pri, exp / 2) % fec * epow(pri, exp / 2) % fec) % fec;//对于幂的奇数和偶数的情况分开判断
//    if (exp & 1) {
//        result = (pri * epow(pri, (exp - 1) / 2) % fec * epow(pri, (exp - 1) / 2) % fec) % fec;
//    } else {
//        result = (epow(pri, exp / 2) % fec * epow(pri, exp / 2) % fec) % fec;
//    }

    return result;
}


int main() {
    ios::sync_with_stdio(false);
//    ifstream in("../test.in");
    int n;
    cin>>n;
    vector< vector<unsigned long long> > matrix(25);
    for(int i=0;i<25;i++){//开一个25*n的数组用来存每个数的对应素数的指数并初始化为0
        matrix[i]=*new vector<unsigned long long> (n+2);
        fill(matrix[i].begin(),matrix[i].end(),0);
    }
    int m;
    cin>>m;
    int l,r,c,b;
    vector<unsigned long long> temp;
    while(m--){
        cin>>l>>r>>c>>b;
        temp=factorization(c,b);//得到素数拆分(连同幂b的计算
        for(int i=0;i<25;i++){
            if(temp[i]==0)continue;
            matrix[i][l]+=temp[i];//差分数组的区间设置
            matrix[i][r+1]-=temp[i];
//            for(int j=l;j<=r;j++){
//                matrix[i][j]+=temp[i];
//            }
        }
    }
    temp=*new vector<unsigned long long>(25);//重(chong)用temp(不能浪费了)
    long long min=LONG_LONG_MAX;//自己的烂摊子要自己收拾,从差分数组中找出原数组最小值
    long long sum=0;
    for(int i=0;i<25;i++){
        min=LONG_LONG_MAX;
        sum=0;
        for(int j=1;j<=n;j++){
            sum+=matrix[i][j];
            if(sum<min)min=sum;
        }
        temp[i]= min;
    }
    unsigned long long ans=1;
    for(int i = 0; i < 25; i++)//利用自定义的带有模运算快速幂计算最终结果
        ans = (ans * epow(prime[i], temp[i])) % fec;
    cout<<ans;

    return 0;
}

  • 17
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值