OIER们最近的旅游计划,是到长春净月潭,享受那里的湖光山色,以及明媚的阳光。你作为整个旅游的策划者和负责人,选择在潭边的一家著名的旅馆住宿。这个巨大的旅馆一共有N (1 <= N <= 50000)间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的潭面。 所有的旅游者,都是一批批地来到旅馆的服务台,希望能订到Di (1 <= Di <= N)间连续的房间。服务台的接待工作也很简单:如果存在r满足编号为r…r+Di-1的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的r,服务员会选择其中最小的一个。 旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字Xi、Di描述,表示编号为Xi…Xi+Di-1 (1 <= Xi <= N-Di+1)房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。 你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理M (1 <= M <=50000)个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。
线段树裸题,不用多讲,上代码吧。
#include
const int MAXN = 50005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m;
int max(int x,int y) {
if(x<y) return y;
return x;
}
struct node
{
int l,r,fr,ba,mi,loc,Max,lazy;
node operator + (const node &x) const {
return node{l,x.r,fr+(fr==r-l+1)*x.fr,x.ba+(x.ba==x.r-x.l+1)*ba,ba+x.fr,r-ba+1,max(max(Max,x.Max),ba+x.fr)};
}
}tr[MAXN*4];
void build(int i,int l,int r)
{
if(l==r)
{
tr[i]=node{l,r,1,1,1,1,1,0};
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tr[i]=tr[i<<1]+tr[i<<1|1];
}
void modify(node &x,int op)
{
int len=x.r-x.l+1;
if(op==0) x=node{x.l,x.r,0,0,0,0,0,-1};
else x=node{x.l,x.r,len,len,len,x.l,len,1};
}
void down(int i)
{
if(!tr[i].lazy) return ;
if(tr[i].lazy==-1)
modify(tr[i<<1],0),modify(tr[i<<1|1],0);
else
modify(tr[i<<1],1),modify(tr[i<<1|1],1);
tr[i].lazy=0;
}
void updata(int i,int l,int r,int x)
{
if(l<=tr[i].l && tr[i].r<=r)
{
if(x==0) modify(tr[i],0);
else modify(tr[i],1);
return ;
}
if(tr[i].l>r || l>tr[i].r) return ;
down(i);
updata(i<<1,l,r,x);
updata(i<<1|1,l,r,x);
tr[i]=tr[i<<1]+tr[i<<1|1];
}
int query(int i,int x)
{
int t;
if(tr[i].Max<x) return 0;
if(tr[i].fr>=x) return tr[i].l;
if(t=query(i<<1,x)) return t;
if(tr[i].mi>=x) return tr[i].loc;
if(t=query(i<<1|1,x)) return t;
if(tr[i].ba>=x) return tr[i].r-tr[i].ba+1;
return 0;
}
int main()
{
n=read();m=read();
build(1,1,n);
while(m--)
{
int op=read();
if(op==1)
{
int x=read(),t=0;
printf("%d\n",t=query(1,x));
if(t)
updata(1,t,t+x-1,0);
}
else
{
int x=read(),d=read();
updata(1,x,x+d-1,1);
}
}
return 0;
}
题目:
题目描述
给定n个正整数的序列a1,a2,a3…an,对该序列可执行下面的操作: 选择一个大于k的正整数ai,将ai的值减去1;选择ai-1或ai+1中的一个值加上1。 共给出m个正整数k,对于每次给定的正整数k,经过以上操作一定次数后,求出最长的一个连续子序列,使得这个子序列的每个数都不小于给定的k
输入格式
第一行两个正整数n和m。 第二行n个正整数,第i个正整数表示ai ; 第三行m个正整数,第i个正整数表示第i次给定的k。
输出格式
共一行,输出m个正整数,第i个数表示对于第i次给定的k,经过一定次数操作后,最长连续子序列的长度。
数据范围:
对于 30%的数据, n < 30 n<30 n<30
对于 50%的数据, n < = 2000 n<=2000 n<=2000
对于 100%的数据, n < = 1 , 000 , 000 , m < = 50 , k < = 1 0 9 , a i < = 1 0 9 n<=1,000,000 ,m<= 50, k <= 10^9, ai <= 10^9 n<=1,000,000,m<=50,k<=109,ai<=109
0x01 暴力
首先翻译一下题目,我们需要求一段区间 [ l , r ] [l,r] [l,r],使得 s u m [ r ] − s u m [ l ] − ( r − l + 1 ) ∗ x ≥ 0 sum[r]-sum[l]-(r-l+1)*x\geq 0 sum[r]−sum[l]−(r−l+1)∗x≥0,因为只要一个区间和大于等于给定的平均值,通过若干操作,就能使它满足条件。
那很显然有一个暴力的做法,求出前缀和后枚举就行了。
0x02 单调栈
我们发现题目要求的复杂度是 O ( n ∗ m ) O(n*m) O(n∗m)或 O ( n ∗ m ∗ l o g n ) O(n*m*logn) O(n∗m∗logn)。
但我们发现现在难以优化,我们尝试构造题目的单调性。
设 v a l = s u m [ i ] − x × i val=sum[i]-x\times i val=sum[i]−x×i,我们维护一个 v a l val val单调下降的栈,对于每一个元素,将它 l o w e r _ b o u n d lower\_bound lower_bound,找到第一个不大于它的数,更新答案即可。
但发现还是有点卡,我们尝试优化成 O ( n ∗ m ) O(n*m) O(n∗m)。
我们先维护一个单调栈,然后从后往前跑,在单调栈中找到第一个不大于它的数,只是这里我们对栈顶进行 p o p pop pop,直到不满足 s u m [ i ] − s u m [ s [ t o p ] ] > = 0 sum[i]-sum[s[top]]>=0 sum[i]−sum[s[top]]>=0跳出,最后用 t o p + 1 top+1 top+1更新即可。
为什么能这样做了,原因是对于当前元素不优的话,那么对于前面的元素也一定不优(因为右端点是递减的),这时候满足了一个单调性,就可以这样做了。
0x03 代码
#include
#include
using namespace std;
#define LL long long
const int MAXN = 1000005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,x,s[MAXN],a[MAXN];
LL b[MAXN];
int max(int a,int b)
{
if(a<b) return b;
return a;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
while(m--)
{
int ans=0,top=0,x=read();
s[top=1]=0;
for(int i=1;i<=n;i++)
{
b[i]=b[i-1]+a[i]-x;
if(b[s[top]]-b[i]>0) s[++top]=i;
}
for(int i=n;i>=1;i--)
{
while(top && b[i]-b[s[top]]>=0) top--;
ans=max(ans,i-s[top+1]);
}
printf("%d ",ans);
}
}