FZU ACM 寒假集训3

Easy1:

思路:对每次输入进行比较,若是”insert”,则在优先队列中插入给定数,若是”extract”,则先输出优先队列中的最大数,然后删除它,否则,结束输入。

代码:

#include <iostream>

#include <queue>

using namespace std;

int main(){

    string opt;

    priority_queue<int> a;

    while (1){

        cin >> opt;

        if (opt=="insert"){

            int temp;

            cin >> temp;

            a.push(temp);

        }

        else if (opt=="extract"){

            cout << a.top() << endl;

            a.pop();

        }

        else return 0;

    }

}

学习总结:熟悉优先队列的函数使用方法。

Easy2:

思路:先算出ST表的行数,第x行y列的数据表示在原始数据中[y,y+2^x-1]区间内数据的最大值,所以先制作出完整的ST表,然后再计计算出log2(b-a+1),这个值就是寻找的最大值在ST表中的行数len,再对ST表中len行a列和len行(b-2^len+1)列的数取出最大值,就是原始数据中[a,b]区间内的最大值。

代码:

#include <iostream>

#include <cmath>

using namespace std;

inline int read()

{

int x=0,f=1;char ch=getchar();

while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}

while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}

return x*f;

}

int ST[20][100001];

int main(){

    int n,m,i,j;

    n=read(),m=read();

    int row=log2(n);

    for (i=1;i<=n;i++) ST[0][i]=read();

    for (i=1;i<=row;i++)

        for (j=1;j<=n-(1<<i)+1;j++)

            ST[i][j]=max(ST[i-1][j],ST[i-1][j+(1<<i-1)]);

    for (i=0;i<m;i++){

        int a,b;

        a=read(),b=read();

        int len=log2(b-a+1);

        printf("%d\n",max(ST[len][a],ST[len][b-(1<<len)+1]));

    }

    return 0;

}

学习总结:熟悉ST表的基本模板。

Middle1:

思路:要想耗费体力值最小,就要每次合并的都是个数最少的两堆,那么创建小根堆,把最小的两堆加为一堆后再将这一对加入容器中,不断重复此过程,直到容器中只剩下一堆,此时,计数器中的数字即为答案。

代码:

#include <iostream>

#include <queue>

using namespace std;

int main(){

    int n,i,temp;

    cin >> n;

    priority_queue<int, vector<int>,greater<int> > p;

    for(i=0;i<n;i++){

        cin >> temp;

        p.push(temp);

    }

    int ans=0;

    while (p.size()>1){

        int number1=p.top();

        p.pop();

        int number2=p.top();

        p.pop();

        ans+=number1+number2;

        p.push(number1+number2);

    }

    cout << ans;

    return 0;

}

学习总结:熟悉小根堆用法,深化对优先队列的理解。

Middle2:

思路:将约瑟夫环转换成队列,队列中,每遇到一个人,计数器加一,若计数器等于m,就淘汰队首的人,输出他的编号,并让计数器重跑,否则,则把队首的人放到队尾,如此循环,知道队伍中没人。

代码:
#include <iostream>

#include <queue>

using namespace std;

int main(){

    int n,m,i;

    cin >> n >> m;

    queue<int> a;

    for (i=1;i<=n;i++) a.push(i);

    int count=1;

    while (a.size()>0){

        if (count==m){

            cout << a.front() << ' ';

            a.pop();

            count=1;

        }

        else {

            a.push(a.front());

            a.pop();

            count++;

        }

    }

    return 0;

}

学习总结:按环依次报数可以转换成队列问题来解决。

Hard1:

思路:从最后一头牛开始倒序遍历,并将牛的编号放入一个单调栈:如果当前牛的身高大于栈顶牛的身高,就将栈顶弹出,循环上一步骤,结束后,如果栈内变空,则该牛的右边没有比它高的牛,即它没有仰望对象,如果栈内不为空,则该牛的仰望对象就是栈顶牛,然后将该牛放入栈顶,如此循环,只到遍历完全部的牛。

代码:

#include <iostream>

#include <stack>

using namespace std;

int main(){

    int n,i,pos;

    int h[100002],ans[100002]={0};

    cin >> n;

    stack<int> a;

    for (i=1;i<=n;i++) cin >> h[i];

    for (i=n;i>0;i--) {

        while (!a.empty() && h[a.top()]<=h[i]) a.pop();

        if (a.empty()) ans[i]=0;

        else ans[i]=a.top();

        a.push(i);

    }

    for (i=1;i<=n;i++) cout << ans[i] << endl;

    return 0;

}

学习总结:遇到需要处理的数据是单调不减或单调不增的情况时,可以使用单调栈处理。

Hard2:

思路:首先,拆环成链,把士兵中终点小于起点的,将终点+m,再将每个士兵的终点进排序,然后复制链和士兵的奔袭区间,那么就以指定士兵的左端点l0为起点,以l0+m为终点,找到的士兵奔袭区间能覆盖[l0,l0+m]区间中的所有点,就可以求出所需的最少战士。现以下i+1士兵的左端点<=i士兵的右端点这个条件求出i士兵的下一个士兵i+1,i+1士兵还应满足r(i+1)尽量大,从而满足最少士兵的条件。有所有士兵的下一个士兵后,就用倍增可以求出第i个士兵后第2^j个士兵。要想满足国旗计划,就应该满足最后一个士兵的右端点-第一个士兵的左端点>=m,以DATA[f[temp][j]].r-DATA[i].l<m条件下求出的答案不完整,因为在该条件下所选士兵必然覆盖不了m个点,所以还需最后一名士兵完成最后一个站点,并且还需要计算选定的第一个士兵。

代码:

#include <bits/stdc++.h>

using namespace std;

const int N=2e5+3;

int n,m,i,j,f[2*N][30],ans[N];

struct data{

    int id;

    int l;

    int r;

}DATA[2*N];

int cmp (data x,data y){

    return x.r<y.r;

}

int main(){

    

    cin >> n >> m;

    for (i=1;i<=n;i++){

        DATA[i].id=i;

        int a,b;

        cin >> a >> b;

        if (b<a) b+=m;

        DATA[i].l=a,DATA[i].r=b;

    }

    sort(DATA+1,DATA+1+n,cmp);

    for (i=1;i<=n;i++){

        DATA[i+n].id=0,

        DATA[i+n].l=DATA[i].l+m,

        DATA[i+n].r=DATA[i].r+m;

    }

    int p1=1,p2=1;

    for (;p1<2*n;p1++){

        while (p2<=2*n){

            if (DATA[p1].r<DATA[p2].l) break;

            else p2++;

        }

        p2--;

        f[p1][0]=p2;

    }

    for(j=1;(1<<j)<=2*n;j++)

     for(i=1;i<=2*n;i++)

     if(f[i][j-1]) f[i][j]=f[f[i][j-1]][j-1];

    for (i=1;i<=n;i++){

        int count=0,temp=i;

        for (j=29;j>=0;j--){

            if (f[temp][j]>0 && DATA[f[temp][j]].r-DATA[i].l<m){

                temp=f[temp][j];

                count+=(1<<j);

            }

        }

        ans[DATA[i].id]=count+2;

    }

    for (i=1;i<=n;i++) cout << ans[i] << ' ';

    return 0;

}

学习总结:在成环问题中,可以拆解为链来做。在需要选出多个区间满足条件时,可以使用倍增实现,从而减低时间复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值