題意:給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位爲終點的長度爲某個定值的最大最小和平均值 這正是單調隊列可以解決的經典問題 每次如果隊頭元素位置過小就從隊頭彈出 然後如果隊尾的元素比當前元素大或小就彈出隊尾 在隊尾插入當前元素
總結一下 單調隊列一般用來解決那些線性時間查詢某個區間的最值問題 單調棧的話一般是詢問以某一點爲中心 向左或向右尋找第一個比他大或者小的元素的位置