题意:给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位为终点的长度为某个定值的最大最小和平均值 这正是单调队列可以解决的经典问题 每次如果队头元素位置过小就从队头弹出 然后如果队尾的元素比当前元素大或小就弹出队尾 在队尾插入当前元素
总结一下 单调队列一般用来解决那些线性时间查询某个区间的最值问题 单调栈的话一般是询问以某一点为中心 向左或向右寻找第一个比他大或者小的元素的位置