队列是一种先进先出的线性数据结构,一般来讲,元素从右端进入队列,从左端离开队列。
队列
维护两个队列,其中一个维护入队的小组编号,该队伍第一个人入队时,加入编号,最后一个人出队时删除编号;另一个队列维护每一个小组当前入队的人员信息。
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
#define MAX_N 1000000
#define MAX_M 1000
int id_team[MAX_N+5];
int cnt[1005];
int t,g=1;
int main()
{
while(1)
{
cin>>t;
if(t==0)break;
int n,x;
for(int i=1;i<=t;i++)
{
cin>>n;
for(int j=1;j<=n;j++)
{
scanf("%d",&x);
id_team[x]=i;
}
}
memset(cnt,0,sizeof cnt);
queue<int>team;
queue<int>person[1005];
cout<<"Scenario #"<<g++<<endl;
string s;
while(1)
{
cin>>s;
if(s=="STOP")break;
if(s=="ENQUEUE")
{
cin>>x;
if(!cnt[id_team[x]])team.push(id_team[x]);
person[id_team[x]].push(x);
cnt[id_team[x]]+=1;
}
else
{
int r=team.front();
if(cnt[r]==1)team.pop();
printf("%d\n",person[r].front());
person[r].pop();
cnt[r]-=1;
}
}
cout<<endl;
}
return 0;
}
首先易想出复杂度为
O
(
(
n
+
m
)
l
o
g
(
n
+
m
)
)
O((n+m)log(n+m))
O((n+m)log(n+m))的做法:
维护一个变量delta表示整个集合的偏移量,集合中的数加上delta是他的真实数值,这样可以避免每次给集合中每一个数都加上q。
1.去除集合中的最大值x,令
x
=
x
+
d
e
l
t
a
x=x+delta
x=x+delta
2.把
⌊
p
x
⌋
−
d
e
l
t
a
−
q
和
x
−
⌊
p
x
⌋
−
d
e
l
t
a
−
q
\lfloor px \rfloor-delta-q和x-\lfloor px \rfloor -delta-q
⌊px⌋−delta−q和x−⌊px⌋−delta−q插入集合
3.令
d
e
l
t
a
=
d
e
l
t
a
+
q
delta=delta+q
delta=delta+q
然而本题数据范围过大,需要线性做法更快求解。
对于
x
1
≥
x
2
x1 \geq x2
x1≥x2,令
l
1
,
r
1
,
l
2
,
r
2
l1,r1,l2,r2
l1,r1,l2,r2分别为两者的左右两部分的大小,如果
l
1
≥
l
2
,
r
1
≥
r
2
l1 \geq l2,r1 \geq r2
l1≥l2,r1≥r2,那么便可以得到序列L和R单调的结论,这样便可以每次以
O
(
1
)
O(1)
O(1)取出一个最大值。
l
1
≥
l
2
:
l1\geq l2:
l1≥l2:
⌊
p
x
1
⌋
+
q
=
⌊
p
x
1
+
q
⌋
≥
⌊
p
(
x
2
+
q
)
⌋
=
⌊
p
x
2
+
p
q
⌋
\lfloor px1 \rfloor +q= \lfloor px1 +q\rfloor \geq \lfloor p(x2+q) \rfloor= \lfloor px2+pq \rfloor
⌊px1⌋+q=⌊px1+q⌋≥⌊p(x2+q)⌋=⌊px2+pq⌋
因为
x
1
≥
x
2
,
q
≥
p
q
x1 \geq x2,q\geq pq
x1≥x2,q≥pq,所以上式成立
r
1
≥
r
2
:
r1\geq r2:
r1≥r2:
x
1
−
⌊
p
x
1
⌋
+
q
≥
x
2
−
⌊
p
x
2
⌋
+
q
≥
x
2
−
⌊
p
(
x
2
+
q
)
⌋
+
q
x1-\lfloor px1 \rfloor +q \geq x2-\lfloor px2 \rfloor+q\geq x2-\lfloor p(x2+q) \rfloor+q
x1−⌊px1⌋+q≥x2−⌊px2⌋+q≥x2−⌊p(x2+q)⌋+q
所以算法的时间复杂度 O ( m + n l o g n ) O(m+nlogn) O(m+nlogn)。
#include<iostream>
#include<queue>
#include<algorithm>
#include<limits.h>
using namespace std;
#define MAX_N 100000
int n,m,q,v,u,t;
int s1[MAX_N+5];
int cnt1=1;
int delta=0;
queue<int>s2,s3;
bool cmp(int a,int b)
{
return a>b;
}
int get_max()
{
int m=INT_MIN;
if(cnt1<=n)m=max(m,s1[cnt1]);
if(s2.size())m=max(m,s2.front());
if(s3.size())m=max(m,s3.front());
if(cnt1<=n&&m==s1[cnt1])cnt1+=1;
else if(s2.size()&&m==s2.front())s2.pop();
else s3.pop();
return m;
}
int main()
{
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
for(int i=1;i<=n;i++)
scanf("%d",s1+i);
sort(s1+1,s1+n+1,cmp);
for(int i=1;i<=m;i++)
{
int x=get_max()+delta;
if(i%t==0)printf("%d ",x);
int l=1ll*x*u/v;
int r=x-l;
delta+=q;
s2.push(l-delta);
s3.push(r-delta);
}
puts(" ");
for(int i=1;i<=n+m;i++)
{
int x=get_max()+delta;
if(i%t==0)printf("%d ",x);
}
puts(" ");
return 0;
}
这个问题很难直接求解,我们反过来求导,把N个数从小到大排序,分成尽可能少的段,让每一段对应原问题一个双端队列。
依次取出排序后的所有数在原始数组A的下标,构成一个新的数组B,B中满足单谷性质的一段对应原问题的一个双端队列。
如果A‘中含有几组相等的数,则可以把相等的数看作一个区间。
我们可以用一个变量记录当前序列处于递增还是递减状态,并用贪心策略尝试把当前区间递增或递减的接在序列末尾。
#include<iostream>
#include<algorithm>
#include<limits.h>
using namespace std;
typedef pair<int,int> PII;
#define MAX_N 200000
PII a[MAX_N+5];
int n;
bool cmp(PII a,PII b)
{
return a.first<b.first;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].first);
a[i].second=i;
}
sort(a+1,a+1+n);
int ret=1,dir=-1,last=INT_MAX;
for(int i=1;i<=n;)
{
int j=i;
while(j<=n&&a[i].first==a[j].first)j++;
int minp=a[i].second,maxp=a[j-1].second;
if(dir==-1)
{
if(last>maxp)last=minp;
else dir=1,last=maxp;
}
else{
if(last<minp)last=maxp;
else{
dir=-1;
ret+=1;
last=minp;
}
}
i=j;
}
cout<<ret;
return 0;
}
单调队列
求前缀和数组sum,枚举sum的每一个位置i为序列的右端点,求左侧符合序列长度的值最小的位置j,求sum[i]-sum[j],其中最大的值就是答案,维护区间最值使用单调队列。
#include<iostream>
#include<queue>
using namespace std;
#define MAX_N 300000
long long sum[MAX_N+5];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%lld",sum+i);
sum[i]+=sum[i-1];
}
deque<int>q;
long long ans=-10000000000;
for(int i=1;i<=n;i++)
{
while(!q.empty()&&sum[q.back()]>sum[i-1])q.pop_back();
q.push_back(i-1);
if(i-q.front()==m+1)q.pop_front();
ans=max(ans,sum[i]-sum[q.front()]);
}
cout<<ans;
}