【BZOJ】5220: [Lydsy2017省队十连测]异或与区间加 多重分块&分类讨论

版权声明:转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/82765071

传送门:bzoj5220


题意

给定长度为 n n 的数组 a a 中不超过 n 2 n^2 个区间(以 l , r l,r 满足异或和为 k k 的形式给出),再给定初始值均为 0 0 的长度为 n n 的数组 b b m m ( s t , e d , w ) (st,ed,w) 的加值操作,具体操作为:遍历所有给定的区间,若满足 l s t , r e d l\geq st,r\leq ed 的区间,则将 b l , b l + 1 , . . . , b r b_l,b_{l+1},...,b_r 全部加上 w w
求操作完毕后的 b b 数组。

n , m 150000 , a i < 2 30 n,m\leq 150000,a_i< 2^{30}


题解

这道题真的是将分块&分类讨论进行到底啊。不仅操作分块,赋值也要分情况讨论,最终将复杂度优化到 O ( ( n + m ) n ) O((n+m)\sqrt n) 。膜了膜 C l a r i s Claris 的题解,终于码出来了。

发现网上没什么题解,下面具体讲一下探索解法的过程:

首先考虑如何快速处理出所有满足 a l x o r a l + 1 . . . x o r a r = k a_l xora_{l+1}...xora_r=k 的区间。设 t i = a 1 x o r a 2 . . . x o r a i t_i=a_1xora_2...xora_i ,条件转化为 t r x o r t l 1 = k t_rxort_{l-1}=k ,逐个加入 t i t_i ,存在 m a p map 里,就可以 n l o g n nlogn 处理出来了,记得记录每个位置对应的 k x o r t i kxor{t_i} 的个数(之后的分类讨论会用到)。

发现这样区间数目最坏情况下多达 n 2 n^2 级别(如全为 k k 的情况)。而加值操作固定为 m m 次。那么考虑牺牲加值操作的复杂度,使得 b b 数组的区间加操作复杂度变为 O ( 1 ) O(1)

可以将加值操作按 e d ed 降序排序,倒序遍历 r = i r=i 的区间,每次将 e d i ed\geq i 的加值操作更新到数组中。而对于每次区间加 [ l , r ] [l,r] 的询问,就是查询中 s t l st\leq l s t i l w i \sum \limits_{st_i\leq l}w_i 。将数组分块,维护加值操作即可。

区间加操作显然可以转换为差分,最后答案求个前缀和即可。这样加值操作总共是 O ( m n ) O(m\sqrt n) 的,单个区间加操作是 O ( 1 ) O(1) 的。

下面到了分类讨论环节:

为保证复杂度,将区间加右端点为 r r 对应的可行 l l 个数 n \geq \sqrt n < n <\sqrt n 的情况分别处理。

扫描二维码关注公众号,回复: 3359845 查看本文章
  • 可行 l < n l<\sqrt n 。直接遍历 l l ,总复杂度 O ( n n ) O(n\sqrt n)
  • 可行 l s q r t n l\geq sqrt n 。直接遍历 l l 复杂度无法保证,而这样的情况出现次数同样 n \leq \sqrt n 次,那么将所有满足 t l = t l , t r = t r t_{l'}=t_l,t_{r'}=t_r 的区间 [ l , r ] [l,r] 提出来,按照以下方法 O ( n + m ) O(n+m) 处理,总复杂度 O ( ( n + m ) n ) O((n+m)\sqrt n)

已经将所有 t l , t r t_l,t_r 相同且出现次数 n \geq\sqrt n 的集合提取出来了,现在逐个集合进行处理。

将加值操作拆成 ( s t 1 , w ) , ( e d , w ) (st-1,-w),(ed,w) ,设后缀和为 S [ 5 ] S[5] S [ 5 ] [ p o s ] S[5][pos] 即为所有 l p o s r l\leq pos\leq r 的区间的权值和。

设可行 l l n l nl 个,位置存在 q l ql 数组中,可行 r r n r nr 个,位置存在 q r qr 数组中。那么考虑对于一个区间加操作 [ l , r ] [l,r] b l , b r + 1 b_l,b_{r+1} 究竟加/减了哪些值。

需要维护6种前缀/后缀和,在这里先列出:

  • S [ 0 ] S[0] :前缀可行 l l 个数
  • S [ 1 ] S[1] :前缀可行 r r 个数
  • S [ 2 ] S[2] S [ 2 ] [ p o s ] = i = 1 m [ s t i > p o s ] ( S [ 1 ] [ e d i ] S [ 1 ] [ s t i 1 ] ) × w i S[2][pos]=\sum\limits_{i=1}^m[st_i>pos](S[1][ed_i]-S[1][st_i-1])\times w_i
  • S [ 3 ] S[3] S [ 3 ] [ p o s ] = i = 1 m [ s t i p o s e d i ] S [ 0 ] [ s t i 1 ] × w i S[3][pos]=\sum\limits_{i=1}^m[st_i\leq pos\leq ed_i]S[0][st_i-1]\times w_i
  • S [ 4 ] S[4] S [ 4 ] [ p o s ] = i = 1 n r [ q r i p o s ] S [ 5 ] [ q r i ] S[4][pos]=\sum\limits_{i=1}^{nr}[qr_i\geq pos]S[5][qr_i]
  • S [ 5 ] S[5] S [ 5 ] [ p o s ] = i = 1 m [ s t i p o s e d i ] w i S[5][pos]=\sum\limits_{i=1}^{m}[st_i\leq pos\leq ed_i]w_i

对于每个 b l b_l ,加上 S [ 4 ] [ l ] S[4][l] ,但这样把 s t l , e d r st\geq l,ed\geq r 的也加上了,所以减去 S [ 2 ] [ r ] S[2][r]

对于每个 b r b_r ,减去 S [ 0 ] [ r ] × S [ 5 ] [ r ] S[0][r]\times S[5][r] ,但同样把 s t l , e d r st\geq l,ed\geq r 的也减去了,所以加上 S [ 3 ] [ r ] S[3][r]

S [ 0 S[0 ~ 5 ] 5] 可以 O ( n + m ) O(n+m) 处理出来,多了几遍循环,常数略大。

这样就做完了,代码实现如同上述。

p . s . p.s. 按照 > n >\sqrt n 判断会 T L E . . . TLE... ,玄学调参设成 > 700 >700 就过了。

做这种题一定要有耐心,想清楚流程写在纸上以后再打代码。。。


代码

#include<bits/stdc++.h>
#define RI register
#define gc getchar()
#define si isdigit(cc)
using namespace std;
const int N=150050,mod=1<<30;

int n,m,kk,block;
int kl[400],kr[400],in[N],cg[400],val[N];
int a[N],to[N],dir[N],d[N];
int ban[N],ans[N],s[7][N],ql[N],qr[N],nl,nr;

map<int,int>mp;
queue<int>que;

struct P{
	int st,ed,w;
	bool operator <(const P&A)const{
	   return A.ed<ed;
	}
}op[N];

char cc;
inline void rd(int &x)
{
	cc=gc;x=0;
	for(;!si;cc=gc);
	for(;si;cc=gc) x=x*10+(cc^48);
}

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}

inline void init()
{
	RI int i,j,dif,sim;
	rd(n);rd(m);rd(kk);
	
	block=sqrt(n)+1; 
	for(i=1;i<=n;++i) in[i]=(i-1)/block+1;
	for(i=in[n];i;--i)
	  kl[i]=(i-1)*block+1,kr[i]=block*i;
	kr[in[n]]=min(kr[in[n]],n);
	
	for(i=1;i<=n;++i){
		rd(a[i]);a[i]^=a[i-1];//前缀异或和 
	    sim=mp[a[i]];dif=mp[kk^a[i]];//找到最近的相同的,互补的 
		to[i]=sim;dir[i]=dif;d[i]=d[sim]+1;//s[i]出现的第d[i]次 
		mp[a[i]]=i;
	}
	
	for(i=1;i<=m;++i) 
	 rd(op[i].st),rd(op[i].ed),rd(op[i].w);
	sort(op+1,op+m+1); 
}

//分块加值 
inline void insert(P e)
{
	int i,l=e.st,r=e.ed,lim,ads=e.w;
	if(l!=kl[in[l]] || r<kr[in[l]]){
		lim=min(kr[in[l]],r);
		for(i=l;i<=lim;++i) val[i]=ad(val[i],ads);
	}else cg[in[l]]=ad(cg[in[l]],ads);
	if(in[l]==in[r]) return;
	if(r!=kr[in[r]]){
		for(i=kl[in[r]];i<=r;++i) val[i]=ad(val[i],ads);
	}else cg[in[r]]=ad(cg[in[r]],ads);
	for(i=in[l]+1;i<in[r];++i) cg[i]=ad(cg[i],ads);
}

//<=700  
inline void fwork()
{
	RI int i,j,k,t;
	for(i=n,j=1;i;--i){
 		for(;j<=m && op[j].ed==i;++j) insert(op[j]);
 		
		//推迟处理 
		if(ban[i]) {ban[to[i]]=1;continue;}
		if(d[dir[i]]>700){
		   ban[i]=ban[to[i]]=1;
		   que.push(i);
		   continue;
		}
		
		for(k=dir[i];k;k=to[k]){
			t=ad(val[k+1],cg[in[k+1]]);
			ans[k+1]=ad(ans[k+1],t);
	        ans[i+1]=dc(ans[i+1],t);
		}
		
		//注意特判 
		if(a[i]==kk){
			t=ad(val[1],cg[1]);
			ans[1]=ad(ans[1],t);
			ans[i+1]=dc(ans[i+1],t);
		}
		
	}
}

//>700
inline void pwork()
{
	RI int i,j,x,tp,pa,pb,pc;
	
	for(i=1;i<=m;++i){
	   pa=op[i].st;pb=op[i].ed;pc=op[i].w;
	   s[5][pb]=ad(s[5][pb],pc);
	   s[5][pa-1]=dc(s[5][pa-1],pc);
	}
	for(i=n-1;i;--i) s[5][i]=ad(s[5][i],s[5][i+1]);
	
	for(;!que.empty();){
	    x=que.front();que.pop();
		
		nr=d[x];
		for(tp=x,i=nr;i;--i,tp=to[tp]) qr[i]=tp;
		
		tp=dir[x];nl=d[tp];
		if(a[x]==kk) nl++;
		for(i=nl;i;--i,tp=to[tp]) ql[i]=tp+1;
		if(a[x]==kk) ql[1]=1;
		
		
		s[0][0]=s[1][0]=0;
		pa=pb=1;
		for(i=1;i<=n;++i){
			s[0][i]=s[0][i-1];s[1][i]=s[1][i-1];
		    for(;pa<=nl && ql[pa]==i;++pa) s[0][i]++;
			for(;pb<=nr && qr[pb]==i;++pb) s[1][i]++;
		}
		
		memset(s[2],0,sizeof(int)*(n+3));
		memset(s[3],0,sizeof(int)*(n+3));
		
		for(i=1;i<=m;++i){
			pa=op[i].st;pb=op[i].ed;pc=op[i].w;
			s[2][pa-1]=ad(s[2][pa-1],mul(s[1][pb]-s[1][pa-1],pc));
			pc=mul(pc,s[0][pa-1]);
			s[3][pa]=ad(s[3][pa],pc);
			s[3][pb+1]=dc(s[3][pb+1],pc);
		}
		
		s[2][n+1]=s[4][n+1]=0;pb=nr;
		for(i=n;i;--i){
			s[4][i]=s[4][i+1];s[2][i]=ad(s[2][i],s[2][i+1]);
			for(;pb>0 && qr[pb]==i;pb--) s[4][i]=ad(s[4][i],s[5][i]);
		}
		
		for(i=1;i<=n;++i) s[3][i]=ad(s[3][i],s[3][i-1]);
		
		for(i=1;i<=nl;++i)
			ans[ql[i]]=ad(ans[ql[i]],dc(s[4][ql[i]],s[2][ql[i]]));
		
		for(i=1;i<=nr;++i)
		    ans[qr[i]+1]=ad(ans[qr[i]+1],dc(s[3][qr[i]],mul(s[0][qr[i]],s[5][qr[i]])));
	}
}

inline void print()
{
	for(RI int i=1;i<=n;++i){
		ans[i]=ad(ans[i],ans[i-1]);
		printf("%d ",ans[i]);
	}
}

int main(){
	init();
	fwork();
	pwork();
	print();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/82765071