首页 > 其他分享 >【根号分治】P9212 「蓬莱人形」 题解

【根号分治】P9212 「蓬莱人形」 题解

时间:2023-10-18 09:48:02浏览次数:44  
标签:qt ty int 题解 qb que P9212 mathcal 根号

P9212

看到除法相关容易想到根号分治。

先对 \(x,y\) 进行讨论,不妨令 \(0\le x,y<m\)。

\(x<y\) 时,当满足 \(a_i+y < m\) 或 \(a_i+x\ge m\) 时,即当 \(a_i<m-y\) 或 \(a_i\ge m-x\) 满足 \((a_i+x)\bmod m<(a_i+y)\bmod m\),即 \(a_i \bmod m\in [0,m-y-1]\bigcup[m-x,m-1]\)。

\(x=y\) 时,无解。

\(x>y\) 时,满足 \(a_i+x\ge m\) 且 \(a_i+y<m\) 时成立,即 \(a_i\bmod m\in[0,m-y-1]\bigcap[m-x,m-1]\)。也可以交换 \(x,y\) 后按照方案一计算,然后用总方案数减去它即可。

这时候问题可以转化为每次求当前区间模 \(m\) 小于 \(x\) 的数的个数,先将区间容斥一下,\(w(l,r,u,d,m)=w(1,r,u,d,m)-w(1,l-1,u,d,m)\)。

设定阈值 \(B\)。

当 \(m\le B\) 时,由于 \(m\) 的取值只有 \(B\) 个。可将 \(w\) 看作一个询问,按 \(w\) 的第二维排序,变为插入一个数 ,查询小于 \(x\) 的数的个数,共有 \(\mathcal{O}(nB)\) 次插入,\(\mathcal{O}(q)\) 次查询,若直接使用树状数组维护,复杂度为 \(\mathcal{O}(m\log B+nB\log B)\)。但这样显然是不可以接受的。发现两边差异较大,可以使用分块来平衡。

当 \(m>B\) 时,先按 \(w\) 的第二维排序。每次插入一个数或查询一段区间的和。但是查询是通过枚举商,即会查询 \(\dfrac{qm}{B}\) 次。同理可以使用分块维护块内及块间的前缀和,即可做到修改 \(\mathcal{O}(\sqrt{n})\),查询 \(\mathcal{O}(1)\) 了。

第一个块长取 \(20\),第二个块长取 \(320\),\(B\) 取 \(500\) 可过。

时间复杂度:\(\mathcal{O}(nB+n\sqrt{n}+\dfrac{qm}{B})\)。

代码:

const int N=1e5+10,M=5e5+10,B=520;
int n,Q,cnt,c;
int val[N],f[N],si[N],so[N],bel[N];
int a[N],ans[M];
struct que{
	int x,u,d,m,typ,id;
} qt[M<<2],unt;
vector<que> qa,qb;

bool cmp1(que a,que b){ return a.m!=b.m?a.m<b.m:a.x<b.x; }
bool cmp2(que a,que b){ return a.x<b.x; }
void ins(int p,int x){
	val[p]++;
	for(int i=p;i<=bel[p]*c-1;i++)
		si[i]++;
	for(int i=bel[p];i<=bel[N-1];i++)
		so[i]++;
}
int query(int l,int r){
	int x=bel[l],y=bel[r];
	if(x==y)
		return si[r]-(l%c>0?si[l-1]:0);
	return (si[x*c-1]-(l%c>0?si[l-1]:0))+si[r]+(so[y-1]-so[x]);
}

int main(){
	unt=(que){0,0,0,0,0,0};
	n=read(),Q=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1,l,r,x,y,m;i<=Q;i++){
		l=read(),r=read(),x=read(),y=read(),m=read(); x%=m,y%=m;
		if(x==y) continue;
		int ty;
		if(x>y) ty=-1,swap(x,y),ans[i]=r-l+1;
		else ty=1;
		if(l>1){
			qt[++cnt]=(que){l-1,m-y-1,0,m,ty*-1,i};
			qt[++cnt]=(que){l-1,m-1,m-x,m,ty*-1,i};
		}
		qt[++cnt]=(que){r,m-y-1,0,m,ty,i};
		qt[++cnt]=(que){r,m-1,m-x,m,ty,i};
	}
	qa.pb(unt),qb.pb(unt);
	for(int i=1;i<=cnt;i++){
		if(qt[i].m<=B)
			qa.pb(qt[i]);
		else
			qb.pb(qt[i]);
	}
	sort(qa.begin(),qa.end(),cmp1);
	c=18;
	for(int i=0;i<=B;i++)
		bel[i]=i/c+1;
	for(int i=1,now=0;i<(int)qa.size();i++){
		if(qa[i].m!=qa[i-1].m){
			for(int j=0;j<=B;j++) val[j]=0;
			for(int j=0;j<=30;j++) f[j]=0;
			now=0;
		}
		for(int j=now+1;j<=qa[i].x;j++){
			val[a[j]%qa[i].m]++;
			f[bel[a[j]%qa[i].m]]++;
		}
		now=qa[i].x;
		int res=0;
		for(int j=1;j<bel[qa[i].u];j++) res+=f[j],assert(f[j]<=n);
		for(int j=(bel[qa[i].u]-1)*c;j<=qa[i].u;j++) res+=val[j],assert(val[j]<=n);
		if(qa[i].d>0){
			for(int j=1;j<bel[qa[i].d-1];j++) res-=f[j];
			for(int j=(bel[qa[i].d-1]-1)*c;j<qa[i].d;j++) res-=val[j];
		}
		ans[qa[i].id]+=res*qa[i].typ;
	}
	sort(qb.begin(),qb.end(),cmp2);
	c=sqrt(N-1);
	for(int i=0;i<N;i++)
		bel[i]=i/c+1;
	for(int i=1,now=0;i<(int)qb.size();i++){
		for(int j=now+1;j<=qb[i].x;j++)
			ins(a[j],1);
		now=qb[i].x;
		int l=qb[i].d,r=qb[i].u,res=0;
		for(int j=0;;j++){
			if(l+j*qb[i].m>N-1)
				break;
			res+=query(l+j*qb[i].m,min(r+j*qb[i].m,N-1));
		}
		ans[qb[i].id]+=res*qb[i].typ;
	}
	for(int i=1;i<=Q;i++) write(ans[i]),putchar('\n');
	return 0;
}

标签:qt,ty,int,题解,qb,que,P9212,mathcal,根号
From: https://www.cnblogs.com/Pengzt/p/17771313.html

相关文章

  • 【dp】【竞赛图的性质】ARC163D Sum of SCC 题解
    ARC163D发现这个竞赛图一定能被分为两个集合\(A\),\(B\)。满足\(\forallu\inA,v\inB\),均有\(u\tov\inE\)。答案就是划分这两个集合的方案数。证明:首先,竞赛图缩完点后一定是一条链,对强连通分量进行标号,满足编号小的强连通分量指向编号大的强连通分量。不妨令竞赛图\(G\)......
  • 【dp】【进制】P3464 [POI2007] WAG-Quaternary Balance 题解
    P3464显然的,先将原数变为四进制的数。由于算的是进位/不进位的代价最小值和方案数,容易想到dp。这里假定该四进制数是从高位到低位的,顺序显然是由低位到高位。令\(f_{i,0/1}\)表示第\(i\)位进/不进位的最小代价,\(g_{i,0/1}\)表示的是最小代价下的方案数。转移是简单的......
  • [题解] CF1790E - XOR Tree
    CF1790E-XORTree题意给定一颗无根树,在可以改变任意一个点的点权操作基础上,让树上任意简单路径的异或和不为\(0\),问最少需要多少次操作。思路假设某个点为根,设\(pre_x\)为\(x\)点到根的树上前缀异或和,\(a_x\)为\(x\)的点权,则\(x\)和\(y\)之间简单路径的异或和......
  • [题解]CF514D R2D2 and Droid Army
    思路首先,可以转化题意,找到一个极长的区间\([l,r]\)使得(其中\(mx_i\)表示\([l,r]\)区间中属性\(i\)的最大值):\[\sum_{i=1}^{m}mx_i\leqk\]显然对于这个东西当\(l,r\)发生移动时,是极其好维护的,所以想到双指针。因为\(m\leq5\),所以我们可以直接开\(m\)个ST表......
  • 题解——2023年码谷提高组模拟赛1016
    题解——2023年码谷提高组模拟赛1016一套被各种转来转去的题;参考:https://blog.csdn.net/liuziha/article/details/127353981、https://www.luogu.com.cn/blog/Chen5201314/xiao-nei-bi-sai-1025-zong-jie-ti-xie和https://www.cnblogs.com/Clyfort/articles/0927-test-solutio......
  • 「BZOJ2505」tickets 题解
    preface网上目前还没看到我的方法,就大概讲一下做法solution首先想到贪心,考虑\([l,r]\)的最大次数,一定是找到最小的\(x\)满足\(l\simx\)的位数的和大于等于\(k\),然后递归的求解\([x+1,r]\),易证。还是考虑将\(Query(l,r)\)拆分成\(Query(1,r)\)和\(Query......
  • RTMP dimensions not set问题解决方案
    问题RTMP开始推流,打印错误提示:dimensionsnotset源码位置libavformat\mux.ccaseAVMEDIA_TYPE_VIDEO:if((par->width<=0||par->height<=0)&&!(of->flags&AVFMT_NODIMENSIONS)){av_log(s,A......
  • CF1879F Last Man Standing 题解
    原题翻译观察题目,容易发现当题目难度为\(x\)时一个OIer存活时间为\(h_i\lceil\frac{a_i}{x}\rceil\)发现\(a_i\)较小,所以我们先考虑暴力枚举\(x\in[1,\maxa_i]\),然后把原数组按\(a_i\)排个序,对于每组\(\lceil\frac{a_i}{x}\rceil\)相同的部分统一计算他......
  • visual studio智能提示出现慢的问题解决办法
    VisualStudio智能提示出现慢的问题解决办法如下:清理VisualStudio缓存。通过"文件"→"打开文件或项目"→"取消"→"是,清理所有项目"进行清理。清理VisualStudio实例。通过"文件"→"关闭解决方案"进行清理。重置用户数据。打开VisualStudio的开发人员命令提示符,输入devenv.ex......
  • CF1680F Lenient Vertex Cover 题解
    CF1680FLenientVertexCover题解这道题和「JOISC2014Day3」电压非常类似,或者说就是一道题。题意就是给你一个图,问能否对所有点黑白染色,允许最多一条边的两个顶点都染成黑色。黑白染色后其实就是一个二分图,那如果有一条边的两个顶点染成黑色,就是说去掉该边后,剩下的图为二分......