约会安排 HDU - 4553
原题链接
考察:线段树
思路:
如果做过校门外的树(增强版)那道题,应该可以想到是建立两个线段树.一个维护基友和女神约会时间tr[0],一个维护女神时间tr[1].
对于基友相约t,我们求出tr[0]的t个空白时间的最左端点,然后更新tr[0];对于女神相约t,先求出tr[0]的左端点,没有就求tr[1]的左端点.更新tr[1]与tr[0].对于虚假的学习,两个都做清空操作.
接下来就是考虑怎么求最左端点.这实际上是求出长度至少为t的空闲连续区间(最大连续子段).考虑设置Ls(从左端点出发的最大连续长度),Rs(同理).那么这个空闲区间可能在
- 左子区间内部.
- 左子区间右子区间中间.
- 右子区间内部.
这就是和那道连通村庄一样的求解方式了.
为了方便判断是否可行,可以再维护一个变量sum,指[l,r]区间内最大的连续长度.
此题细节超多,比如push_up与push_down
然后就是存不存在使用的tr[s][1].sum是还未更新的.答案是不存在,我们每次进行相应操作,都会更新相应的tr[s][1],我们怎么修改基友占用时间,都不会对女神线段树造成影响.
Code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010,M = 10;
char op[M];
int n,m;
struct Node{
int l,r,Ls,Rs,sum,tag;//ls以左端点空白时间 ,Rs以右端点空白时间,整个区间的最大连续空白时间
}tr[2][N<<2];//0:女神+基友 1:女神
int get(int u)
{
return tr[0][u].r-tr[0][u].l+1;
}
void push_up(int u,int s)
{
tr[s][u].Ls = tr[s][u<<1].Ls+(tr[s][u<<1].Ls==get(u<<1)?tr[s][u<<1|1].Ls:0);
tr[s][u].Rs = tr[s][u<<1|1].Rs+(tr[s][u<<1|1].Rs==get(u<<1|1)?tr[s][u<<1].Rs:0);
tr[s][u].sum = max(tr[s][u<<1].sum,tr[s][u<<1|1].sum);
tr[s][u].sum = max(tr[s][u<<1].Rs+tr[s][u<<1|1].Ls,tr[s][u].sum);
}
void push_down(int u,int s)
{
if(tr[s][u].tag==1)
{
tr[s][u<<1].Ls = tr[s][u<<1].Rs = tr[s][u<<1].sum = get(u<<1);
tr[s][u<<1|1].Ls = tr[s][u<<1|1].Rs = tr[s][u<<1|1].sum = get(u<<1|1);
tr[s][u<<1].tag = tr[s][u<<1|1].tag = tr[s][u].tag;
tr[s][u].tag = 0;
}
if(tr[s][u].tag==-1)
{
tr[s][u<<1].Ls = tr[s][u<<1].Rs = tr[s][u<<1].sum = 0;
tr[s][u<<1|1].Ls = tr[s][u<<1|1].Rs = tr[s][u<<1|1].sum = 0;
tr[s][u<<1].tag = tr[s][u<<1|1].tag = tr[s][u].tag;
tr[s][u].tag = 0;
}
}
void build(int u,int l,int r)
{
tr[0][u] = {l,r,1,1,1,0}; tr[1][u] = {l,r,1,1,1,0};
if(l==r) return;
int mid = l+r>>1;
build(u<<1,l,mid); build(u<<1|1,mid+1,r);
push_up(u,0); push_up(u,1);
}
int findL(int u,int s,int c)//清空操作会破坏连续性,所以要维护Rs
{
if(tr[s][u].l==tr[s][u].r) return tr[s][u].l;
int mid = tr[s][u].l+tr[s][u].r>>1;
push_down(u,s);
if(tr[s][u<<1].sum>=c) return findL(u<<1,s,c);
else if(tr[s][u<<1].Rs+tr[s][u<<1|1].Ls>=c) return mid-tr[s][u<<1].Rs+1;
else return findL(u<<1|1,s,c);
}
void modify(int u,int l,int r,int s)//清零
{
if(tr[s][u].l>=l&&tr[s][u].r<=r)
{
tr[s][u].Ls = tr[s][u].Rs = tr[s][u].sum = 0;
tr[s][u].tag = -1;
return;
}
push_down(u,s);
int mid = tr[s][u].l+tr[s][u].r>>1;
if(l<=mid) modify(u<<1,l,r,s);
if(mid<r) modify(u<<1|1,l,r,s);
push_up(u,s);
}
void clearT(int u,int l,int r,int s)
{
if(tr[s][u].l>=l&&tr[s][u].r<=r)
{
tr[s][u].Ls = tr[s][u].Rs = tr[s][u].sum = get(u);
tr[s][u].tag = 1;
return;
}
push_down(u,s);
int mid = tr[s][u].l+tr[s][u].r>>1;
if(l<=mid) clearT(u<<1,l,r,s);
if(mid<r) clearT(u<<1|1,l,r,s);
push_up(u,s);
}
int main()
{
int T,kcase=0;
scanf("%d",&T);
while(T--)
{
printf("Case %d:\n",++kcase);
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--)
{
int c,d;
scanf("%s%d",op,&c);
if(op[0]=='D')
{
if(tr[0][1].sum<c) {printf("fly with yourself\n");continue;}
int L = findL(1,0,c);
modify(1,L,L+c-1,0);
printf("%d,let's fly\n",L);
}else if(op[0]=='N')
{
int s = -1;
if(tr[0][1].sum>=c) s = 0;
else if(tr[1][1].sum>=c) s = 1;
if(s==-1) {puts("wait for me");continue;}
int L = findL(1,s,c);
printf("%d,don't put my gezi\n",L);
modify(1,L,L+c-1,1),modify(1,L,L+c-1,0);
}else if(op[0]=='S')
{
scanf("%d",&d);
puts("I am the hope of chinese chengxuyuan!!");
clearT(1,c,d,0); clearT(1,c,d,1);
}
}
}
return 0;
}