并不对劲的复健训练-bzoj4592:p4344:[SHOI2015]脑洞治疗仪
题目大意
有\(n\)个位置,一开始每个位置上都放着一个东西。每个位置上最多放一个东西。
有\(m\)个操作,分为以下三类:
1.给定\(l,r\),将位置\(l\)到位置\(r\)的所有东西都拿走并扔掉。
2.给定\(l_0,r_0,l_1,r_1\),将位置\(l_0\)到位置\(r_0\)的所有东西都拿出来,用来填位置\(l_1\)到位置\(r_1\)的空位,优先填编号小的空位。如果拿出来的东西还有剩的就扔掉。
3.给定\(l,r\),询问位置\(l\)到位置\(r\)中,最长一段连续空位有多长。
\(n,m\leq 2\times 10^5\)
题解
对于第三类操作,可以在线段树的每个点记录该区间靠左端最长连续空位、靠右端最长连续空位、最长连续空位、空位总数,这些信息可以\(\Theta(1)\)合并。
在记录这些信息时,“区间赋值”的标记下传的时间可以是\(\Theta(1)\)。
第二类操作可以看成给把区间\([l_0,r_0]\)赋值为0,再在\([l_1,r_1]\)中二分一个位置\(x\),使\(x\)尽可能大并且\([l_0,x]\)中的空位数不超过\([l_0,r_0]\)拿出的东西,再把\([l_0,x]\)赋值为1.
看上去\([l_1,r_1]\)被拆成了\(log\space n\)个区间,要在每个区间内二分,一次二类操作的总时间是\(Theta(log^2\space n)\)。
但是\(log\space n\)个区间中至多有一个是需要二分的,剩下的不是整个区间都被赋值就是整个区间都不被赋值。所以二类操作的时间是\(\Theta(log\space n)\)
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define LL long long
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define maxn 200007
#define ls (u<<1)
#define rs (u<<1|1)
#define mi ((l+r)>>1)
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
char ch[20];int f=0;
if(!x){putchar('0'),putchar('\n');return;}
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int sum[maxn<<2],lx[maxn<<2],rx[maxn<<2],mx[maxn<<2],mk[maxn<<2],ad,pref;
int n,m;
void pu(int u,int l,int r)
{
sum[u]=sum[ls]+sum[rs];
if(sum[ls]==mi-l+1)lx[u]=(mi-l+1)+lx[rs];
else lx[u]=lx[ls];
if(sum[rs]==r-mi)rx[u]=(r-mi)+rx[ls];
else rx[u]=rx[rs];
mx[u]=max(max(mx[ls],mx[rs]),lx[rs]+rx[ls]);
return;
}
void mark(int u,int l,int r,int k)//k=0,1
{
mk[u]=k;
if(k==0){sum[u]=mx[u]=lx[u]=rx[u]=r-l+1;}
else {sum[u]=mx[u]=lx[u]=rx[u]=0;}
return;
}
void pd(int u,int l,int r){if(mk[u]!=-1)mark(ls,l,mi,mk[u]),mark(rs,mi+1,r,mk[u]),mk[u]=-1;}
int ask(int u,int l,int r,int x,int y)
{
if(x<=l&&r<=y){int tmp=sum[u];mark(u,l,r,0);return r-l+1-tmp;}
pd(u,l,r);int res=0;
if(x<=mi)res+=ask(ls,l,mi,x,y);
if(y>mi)res+=ask(rs,mi+1,r,x,y);pu(u,l,r);
return res;
}
int query(int u,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
int res=pref+lx[u];
pref=(sum[u]==r-l+1)?(pref+sum[u]):rx[u];
return max(res,mx[u]);
}
pd(u,l,r);int res=0;
if(x<=mi)res=query(ls,l,mi,x,y);
if(y>mi)res=max(res,query(rs,mi+1,r,x,y));
return res;
}
void work(int u,int l,int r)
{
if(!ad)return;
if(l==r&&ad>sum[u]){ad-=sum[u],mark(u,l,r,1);return;}
if(l==r)return;
pd(u,l,r);
if(ad>=sum[ls])ad-=sum[ls],mark(ls,l,mi,1),work(rs,mi+1,r);
else work(ls,l,mi);
pu(u,l,r);return;
}
void chg(int u,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
if(ad)
{
if(ad>=sum[u])ad-=sum[u],mark(u,l,r,1);
else work(u,l,r);
}
return;
}
pd(u,l,r);
if(x<=mi)chg(ls,l,mi,x,y);
if(y>mi)chg(rs,mi+1,r,x,y);
pu(u,l,r);return;
}
int main()
{
n=read(),m=read();
memset(mk,-1,sizeof(mk));
while(m--)
{
int f=read();
if(f==0){int l=read(),r=read(),t=ask(1,1,n,l,r);}
else if(f==1)
{
int l0=read(),r0=read(),l1=read(),r1=read();ad=ask(1,1,n,l0,r0);
chg(1,1,n,l1,r1);
}
else
{
int l=read(),r=read();pref=0;
write(query(1,1,n,l,r));
}
}
return (~(0-0)+1);
}
一些感想
教练追求的“最佳训练方式”只是“让一群人中出现尽可能多的拿奖的人的训练方式”,而每个人追求的“最佳训练方式”是“让自己这一个人更厉害的训练方式”。
所以要么不迷信权威(包括但不限于教练、周围看似很厉害的人、路人),要么指听最权威的“权威”的那句“不迷信权威”。