Bootstrap

【原创题】鸽子的序列题解(线段树+整体二分+树状数组)

题目:luogu U71972.
题目大意:给定一个长度为 n n n的序列,要求支持 m m m次操作:
1.格式 1   l ,   r   k 1\,l,\,r\,k 1l,rk,表示修改区间 [ l , r ] [l,r] [l,r]内的所有数变为 k k k.
2.格式 2   l ,   r   k 2\,l,\,r\,k 2l,rk,表示查询区间 [ l , r ] [l,r] [l,r]内第 k k k大的数.
1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq 10^5 1n,m105.

建议做这道题之前先做一下BZOJ1901 Dynamic Rankings,这道题只是把单点修改改成了区间修改.

考虑如何维护区间修改.仍然考虑按照套路维护,每次修改拆成把原来全部变为 0 0 0在变为 k k k.

但是直接每一个点都变为 0 0 0明显时间复杂度不正确,考虑把每一段相同权值的区间放在一个修改中放入操作序列,然后再把一整段修改区间改成 k k k放在一个修改中加入操作序列即可.不过要注意的是这样子整体二分内部维护的树状数组需要支持区修区查.

然后考虑时间复杂度,容易发现只有修改操作的数量是不稳定的,考虑如何计算出修改的数量.

考虑对于每一个修改,它会使得操作序列中多出来的操作数量增加多少,容易发现是它内部含有的不同的色块数量.

考虑每一个修改最多会使不同色块的数量增加的数量,容易发现是下面这种情况达到最多,为 2 2 2
在这里插入图片描述
然后我们考虑对于每个修改,它会把它内部完全包含的变成同一个色块,还剩下旁边最多 2 2 2个色块.算上最后还有 1 1 1个区间修改需要加入,可以均摊出每个修改会在操作序列中增加 2 + 2 + 1 2+2+1 2+2+1个操作.

然后考虑初始序列的贡献,发现最多每一个点 1 1 1个修改,然后会对后面造成最多 1 1 1的影响,也就是说初始一个位置的贡献最多为 1 + 1 = 2 1+1=2 1+1=2.

那么操作序列中的操作数量上界为 O ( 5 m + 2 n ) = O ( m + n ) O(5m+2n)=O(m+n) O(5m+2n)=O(m+n),事实上实践的时候这个界貌似还可以继续下调,但是在 m = n m=n m=n时这个界高于 5 n 5n 5n没事直接开10倍慌啥).

然后我们就要考虑怎么快速维护这些色块了,很明显一个维护区间染色状态的线段树即可解决.

总复杂度为 O ( n log ⁡ n log ⁡ a [ i ] ) O(n\log n\log a[i]) O(nlognloga[i]),美中不足的是这个做法势能上界比较紧导致常数较大,实测 1 0 5 10^5 105级别的数据要跑 1.5 s 1.5s 1.5s左右.

代码如下:

#include<bits/stdc++.h>
  using namespace std;

typedef long long LL;

const int N=300000,INF=(1<<30)-1;

int Ri(){
  int x=0;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar());
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x;
}

int n,m,a[N+9];
int c[2][N+9];

void Add(int t,int p,int v){if (!p) return;for (;p<=n;p+=p&-p) c[t][p]+=v;}
int Query(int t,int p){int res=0;for (;p;p-=p&-p) res+=c[t][p];return res;}
void Add_seg(int L,int R,int v){Add(0,L,v);Add(0,R+1,-v);Add(1,L,v*(L-1));Add(1,R+1,-v*R);}
int Query_seg(int L,int R){return Query(0,R)*R-Query(1,R)-Query(0,L-1)*(L-1)+Query(1,L-1);}

struct tree{
  int l,r,lc,rc,fst,tag;
}tr[N*4+9];

void Pushup(int k){
  int ls=k<<1,rs=k<<1|1;
  tr[k].lc=tr[ls].lc;tr[k].rc=tr[rs].rc;
  if (tr[ls].fst^tr[ls].r) tr[k].fst=tr[ls].fst;
  else if (tr[ls].rc^tr[rs].lc) tr[k].fst=tr[ls].r;
    else tr[k].fst=tr[rs].fst;
}

void Update(int k,int v){tr[k].lc=tr[k].rc=v;tr[k].fst=tr[k].r;tr[k].tag=v;}

void Pushdown(int k){
  if (!tr[k].tag) return;
  Update(k<<1,tr[k].tag);Update(k<<1|1,tr[k].tag);
  tr[k].tag=0;
}

void Build(int L,int R,int k=1){
  tr[k].l=L;tr[k].r=R;
  if (L==R){tr[k].lc=tr[k].rc=a[L];tr[k].fst=tr[k].l;return;}
  int mid=L+R>>1;
  Build(L,mid,k<<1);Build(mid+1,R,k<<1|1);
  Pushup(k);
}

void Change(int L,int R,int v,int k=1){
  if (L==tr[k].l&&R==tr[k].r){Update(k,v);return;}
  Pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) Change(L,R,v,k<<1);
  else if (L>mid) Change(L,R,v,k<<1|1);
    else Change(L,mid,v,k<<1),Change(mid+1,R,v,k<<1|1);
  Pushup(k);
}

int Query_fst(int L,int R,int k=1){
  if (L>R) return 0;
  if (L==tr[k].l&&R==tr[k].r) return tr[k].fst;
  Pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) return Query_fst(L,R,k<<1);
  else if (L>mid) return Query_fst(L,R,k<<1|1);
    else {
      int t=Query_fst(L,mid,k<<1);
      if (t^mid) return t;
      if (tr[k<<1].rc^tr[k<<1|1].lc) return mid;
      return Query_fst(mid+1,R,k<<1|1);
	}
}

int Query_val(int p,int k=1){
  if (tr[k].l==tr[k].r) return tr[k].lc;
  Pushdown(k);
  int mid=tr[k].l+tr[k].r>>1; 
  return p<=mid?Query_val(p,k<<1):Query_val(p,k<<1|1);
}

struct question{
  int opt,a,b,c,d,id;
  question(int Opt=0,int A=0,int B=0,int C=0,int D=0,int Id=0){opt=Opt;a=A;b=B;c=C;d=D;id=Id;}
}q[N*7+9],lq[N*7+9],rq[N*7+9];
int cq,ans[N+9];

void Divide(question *q,int L,int R,int h,int t){
  if (h>t) return;
  if (L==R){
  	for (int i=h;i<=t;++i)
  	  if (!q[i].opt) ans[q[i].id]=L;
  	return;
  }
  int mid=L+R>>1,lt=0,rt=0;
  for (int i=h;i<=t;++i)
    if (q[i].opt){
      if (q[i].c>mid) rq[++rt]=q[i];
      else Add_seg(q[i].a,q[i].b,q[i].d),lq[++lt]=q[i];
	}else{
	  int tmp=Query_seg(q[i].a,q[i].b);
	  if (q[i].c<=tmp) lq[++lt]=q[i];
	  else q[i].c-=tmp,rq[++rt]=q[i];
	}
  for (int i=h;i<=t;++i)
    if (q[i].opt&&q[i].c<=mid) Add_seg(q[i].a,q[i].b,-q[i].d);
  for (int i=1;i<=lt;++i) q[h+i-1]=lq[i];
  for (int i=1;i<=rt;++i) q[h+lt+i-1]=rq[i];
  Divide(q,L,mid,h,h+lt-1);
  Divide(q,mid+1,R,h+lt,t);
}

void into(){
  n=Ri();m=Ri();
  int opt,l,r,x;
  for (int i=1;i<=n;++i){
    a[i]=Ri();
    q[++cq]=question(1,i,i,a[i],1,0);
  }
  Build(1,n);
  for (int i=1;i<=m;++i){
  	opt=Ri();l=Ri();r=Ri();x=Ri();
  	if (opt&1){
  	  for (int t=l,j=Query_fst(l,r);j;t=j+1,j=Query_fst(t,r))
  	    q[++cq]=question(1,t,j,Query_val(j),-1,0);
  	  q[++cq]=question(1,l,r,x,1,0);
  	  Change(l,r,x);
	}else x>r-l+1?ans[i]=INF:q[++cq]=question(0,l,r,x,0,i);
  }
}

void work(){
  Divide(q,1,INF,1,cq);
}

void outo(){
  for (int i=1;i<=m;++i)
    if (ans[i]) ans[i]==INF?puts("INF"):printf("%d\n",ans[i]);
}

int main(){
  into();
  work();
  outo();
  return 0; 
}
;