线段树和单调队列--2018 UESTC Training for Data Structures F - 好吃不过饺子

题意:给n个对象 每个对象有坐标和能量两个变量 有c次查询 每次查询是关于输出每个坐标的能量值与坐标为当前坐标-len到当前坐标-1的所有对象的能量的关系(大于?小于)

相对比较水的一道题 有很多种方法可以做 一开始我是用纯线段树完成的 直接维护每个区间的最大最小和平均值 直接查询 但是错误以为他的平均值是整数向下取整算的 然而实际是用double+误差eps来确定与平均值关系 WA了很多次test 2 受思维定势影响吧:

AC代码:

#include <set>
#include <map>
#include <list>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <bitset>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <fstream>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define eps 1e-6
typedef long long ll;
const int maxn=100005;
const int inf=0x3f3f3f3f;

int a[maxn],b[maxn];
struct node
{
    ll sum;
    int mn,mx;
    int pos;
};
node tree[maxn<<2];

void pushup(int rt)
{
    tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
    tree[rt].mn=min(tree[rt<<1].mn,tree[rt<<1|1].mn);
    tree[rt].mx=max(tree[rt<<1].mx,tree[rt<<1|1].mx);
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        tree[rt].sum=tree[rt].mn=tree[rt].mx=b[l];
        tree[rt].pos=a[l];
        tree[rt].sum=(ll)b[l];
        return ;
    }
    int m=l+(r-l)/2;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}
int bseek(int n,int c,int tp)
{
    int x=1,y=n;
    int m;
    while(x<y)
    {
        m=x+(y-x)/2;
        if(a[m]>=c) y=m;
        else x=m+1;
    }
    if(tp==1&&a[x]>c) x--;
    return x;
}
int mnquery(int le,int re,int l,int r,int rt)
{
    if(le<=l&&re>=r)
    {
        return tree[rt].mn;
    }
    int ret=inf;
    int m=l+(r-l)/2;
    if(le<=m) ret=min(ret,mnquery(le,re,l,m,rt<<1));
    if(re>m) ret=min(ret,mnquery(le,re,m+1,r,rt<<1|1));
    return ret;
}
int mxquery(int le,int re,int l,int r,int rt)
{
    if(le<=l&&re>=r)
    {
        return tree[rt].mx;
    }
    int ret=0;
    int m=l+(r-l)/2;
    if(le<=m) ret=max(ret,mxquery(le,re,l,m,rt<<1));
    if(re>m) ret=max(ret,mxquery(le,re,m+1,r,rt<<1|1));
    return ret;
}
ll smquery(int le,int re,int l,int r,int rt)
{
    if(le<=l&&re>=r)
    {
        return tree[rt].sum;
    }
    ll ret=0;
    int m=l+(r-l)/2;
    if(le<=m) ret+=smquery(le,re,l,m,rt<<1);
    if(re>m) ret+=smquery(le,re,m+1,r,rt<<1|1);
    return ret;
}
int main()
{
    int n,t;
    scanf("%d%d",&n,&t);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
    }
    build(1,n,1);
    while(t--)
    {
        char k[10],fuc[10];
        int len,le,re;
        scanf("%s",k);
        scanf("%s",fuc);
        scanf("%d",&len);
        if(strcmp(k,"gt")==0)
        {
            if(strcmp(fuc,"min")==0)
            {
                int ans=0;
                for(int i=1;i<=n;i++)
                {
                    le=bseek(n,a[i]-len,0);
                    re=bseek(n,a[i]-1,1);
                    if(re<1||re<le) continue;
                    int num=mnquery(le,re,1,n,1);
                    if(b[i]>num) ans++;
                }
                printf("%d\n",ans);
            }
            else if(strcmp(fuc,"max")==0)
            {
                int ans=0;
                for(int i=1;i<=n;i++)
                {
                    le=bseek(n,a[i]-len,0);
                    re=bseek(n,a[i]-1,1);
                    if(re<1||re<le) continue;
                    int num=mxquery(le,re,1,n,1);
                    if(b[i]>num) ans++;
                }
                printf("%d\n",ans);
            }
            else
            {
                int ans=0;
                for(int i=1;i<=n;i++)
                {
                    le=bseek(n,a[i]-len,0);
                    re=bseek(n,a[i]-1,1);
                    if(re<1||re<le) continue;
                    ll num=smquery(le,re,1,n,1);
                    double p=(double)num;
                    p/=(double)(re-le+1);
                    if((double)b[i]>p+eps) ans++;
                }
                printf("%d\n",ans);
            }
        }
        else if(strcmp(k,"lt")==0)
        {
            if(strcmp(fuc,"min")==0)
            {
                int ans=0;
                for(int i=1;i<=n;i++)
                {
                    le=bseek(n,a[i]-len,0);
                    re=bseek(n,a[i]-1,1);
                    if(re<1||re<le) continue;
                    int num=mnquery(le,re,1,n,1);
                    if(b[i]<num) ans++;
                }
                printf("%d\n",ans);
            }
            else if(strcmp(fuc,"max")==0)
            {
                int ans=0;
                for(int i=1;i<=n;i++)
                {
                    le=bseek(n,a[i]-len,0);
                    re=bseek(n,a[i]-1,1);
                    if(re<1||re<le) continue;
                    int num=mxquery(le,re,1,n,1);
                    if(b[i]<num) ans++;
                }
                printf("%d\n",ans);
            }
            else
            {
                int ans=0;
                for(int i=1;i<=n;i++)
                {
                    le=bseek(n,a[i]-len,0);
                    re=bseek(n,a[i]-1,1);
                    if(re<1||re<le) continue;
                    ll num=smquery(le,re,1,n,1);
                    double p=(double)num;
                    p/=(double)(re-le+1);
                    if((double)b[i]<p+eps) ans++;
                }
                printf("%d\n",ans);
            }
        }
    }
}

还有比较好的一种做法是用单调队列 因为每次维护都是维护以当前点-1位为终点的长度为某个定值的最大最小和平均值 这正是单调队列可以解决的经典问题 每次如果队头元素位置过小就从队头弹出 然后如果队尾的元素比当前元素大或小就弹出队尾 在队尾插入当前元素

总结一下 单调队列一般用来解决那些线性时间查询某个区间的最值问题 单调栈的话一般是询问以某一点为中心 向左或向右寻找第一个比他大或者小的元素的位置


猜你喜欢

转载自blog.csdn.net/weixin_39302444/article/details/80542731