線段樹和單調隊列--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位爲終點的長度爲某個定值的最大最小和平均值 這正是單調隊列可以解決的經典問題 每次如果隊頭元素位置過小就從隊頭彈出 然後如果隊尾的元素比當前元素大或小就彈出隊尾 在隊尾插入當前元素

總結一下 單調隊列一般用來解決那些線性時間查詢某個區間的最值問題 單調棧的話一般是詢問以某一點爲中心 向左或向右尋找第一個比他大或者小的元素的位置


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章