洛谷 P6783 - [Ynoi2008] rrusq(KDT+势能均摊+根号平衡)

洛谷题面传送门

首先显然原问题严格强于区间数颜色,因此考虑将询问离线下来然后用某些根号级别复杂度的数据结构。按照数颜色题目的套路,我们肯定要对于每种颜色维护一个前驱 \(pre\),那么答案可写作 \(pre\ge l\) 的所有颜色的权值之和。此题也不例外。考虑将所有询问存在右端点,那么每次扫描一个矩形 \(i\),就将矩形中所有点被覆盖的时间设为 \(i\),那么 \([l,r]\) 区间的答案即为扫描到 \(r\)\(pre\ge l\) 的点权之和。

考虑怎么维护 \(pre\),首先看到矩形以及这个 128M 的空间,显然不是让我们树套树而是让我们 2-DT 的,因此建出 2-DT,那么矩形覆盖即可转化为 \(\mathcal O(\sqrt{n})\) 个单点修改和 \(\mathcal O(\sqrt{n})\) 个子树整体修改,但是由于我们并不是简简单单地求出 \(pre\) 就完事了,我们还要求出 \(pre\) 值在一段区间内的点权之和,事情变得棘手起来……不过注意到这样一件事,对于单点修改显然如果原来有标记那就直接撤销贡献即可,对于子树整体修改,整体覆盖的点的子树都是不交的,而撤销贡献等价于“收回”标记,即打标记的逆操作。因此我们考虑对一个子树整体覆盖时,就暴力 DFS 整棵子树收回子树中所有标记,如果子树中没有标记那就 return,然后假设我们欲将矩形中所有点赋值为 \(v\),那么我们就建立一个数据结构并在这个数据结构下标为 \(v\) 的位置加上当前子树中所有点点权和。由于收回标记是打标记的逆操作,你暴力 DFS 的复杂度均摊下来肯定和打标记相同,因此这部分复杂度是 \(m\sqrt{n}\) 的。

接下来考虑怎样计算答案,通过上面的分析我们已经能够在较快时间内维护 \(pre\) 为某个值的点权之和的变化了,这样扫描到 \(r\) 时,我们只需做一遍区间求和即可求出区间 \([l,r]\) 的答案。那么用什么数据结构维护呢?根据根号平衡的思想,如果用树状数组那么复杂度 \(m\sqrt{n}\log n+q\log n\),导致复杂度向左倾,不知道能不能过。换成 \(\mathcal O(1)\) 单点加 \(\mathcal O(\sqrt{n})\) 求和的分块复杂度就是 \(m\sqrt{n}+q\sqrt{m}\),应该就比较稳了(

const int MAXN=1e5;
const int K=2;
const int BLK=317;
const int MAXQ=1e6;
int n,m,qu;
struct point{
	int x[K+2],v;
	point(){memset(x,0,sizeof(x));v=0;}
	int& operator [](int id){return x[id];}
} p[MAXN+5];
struct node{int ch[2],tg,has_tag,tg_pt;ll sum;point val,mn,mx;} s[MAXN+5];
void pushup0(int k){s[k].sum=s[s[k].ch[0]].sum+s[s[k].ch[1]].sum+s[k].val.v;}
void pushup(int k){s[k].has_tag=s[s[k].ch[0]].has_tag|s[s[k].ch[1]].has_tag|(s[k].tg>0)|(s[k].tg_pt>0);}
int ncnt=0,rt=0;
void build(int &k,int l,int r){
	if(l>r) return;k=++ncnt;
	static double avg[K+2],var[K+2];
	fill(avg,avg+K,0);fill(var,var+K,0);
	for(int i=l;i<=r;i++) for(int j=0;j<K;j++) avg[j]+=p[i][j];
	for(int j=0;j<K;j++) avg[j]/=(r-l+1);
	for(int i=l;i<=r;i++) for(int j=0;j<K;j++) var[j]+=(p[i][j]-avg[j])*(p[i][j]-avg[j]);
	double mx=0;int dim=0;
	for(int j=0;j<K;j++) if(var[j]>mx) mx=var[j],dim=j;
	int mid=l+r>>1;nth_element(p+l,p+mid,p+r+1,[&](point x,point y){return x[dim]<y[dim];});
	build(s[k].ch[0],l,mid-1);build(s[k].ch[1],mid+1,r);
	s[k].val=s[k].mn=s[k].mx=p[mid];
//	printf("node %d %d %d %d\n",k,p[mid][0],p[mid][1],p[mid].v);
//	printf("%d %d %d\n",k,s[k].ch[0],s[k].ch[1]);
	for(int j=0;j<K;j++){
		if(s[k].ch[0]) chkmin(s[k].mn[j],s[s[k].ch[0]].mn[j]),chkmax(s[k].mx[j],s[s[k].ch[0]].mx[j]);
		if(s[k].ch[1]) chkmin(s[k].mn[j],s[s[k].ch[1]].mn[j]),chkmax(s[k].mx[j],s[s[k].ch[1]].mx[j]);
	} pushup0(k);
}
struct blk_ds{
	ll blk_sum[BLK+5],val[MAXN+5];
	int blk_cnt,blk_sz,bel[MAXN+5],L[BLK+5],R[BLK+5];
	void init(){
		blk_sz=(int)sqrt(m);blk_cnt=(m-1)/blk_sz+1;
		for(int i=1;i<=blk_cnt;i++){
			L[i]=(i-1)*blk_sz+1;R[i]=min(i*blk_sz,m);
			for(int j=L[i];j<=R[i];j++) bel[j]=i;
		}
	}
	void add(int x,ll v){/*printf("add %d %lld\n",x,v);*/blk_sum[bel[x]]+=v;val[x]+=v;}
	ll query(int l,int r){
		if(bel[l]==bel[r]){
			ll sum=0;
			for(int i=l;i<=r;i++) sum+=val[i];
			return sum;
		} else {
			ll sum=0;
			for(int i=l;i<=R[bel[l]];i++) sum+=val[i];
			for(int i=bel[l]+1;i<bel[r];i++) sum+=blk_sum[i];
			for(int i=L[bel[r]];i<=r;i++) sum+=val[i];
			return sum;
		}
	}
} B;
struct bar{int x1,y1,x2,y2;} b[MAXN+5];
vector<pii> qv[MAXN+5];
ll res[MAXQ+5];
bool dfscheck(int k);
void clrtag(int k){
	if(!s[k].has_tag||!k) return;
	if(s[k].tg) B.add(s[k].tg,-s[k].sum),s[k].tg=0;
	if(s[k].tg_pt) B.add(s[k].tg_pt,-s[k].val.v),s[k].tg_pt=0;
	clrtag(s[k].ch[0]);clrtag(s[k].ch[1]);s[k].has_tag=0;
}
void pushdown(int k){
	if(s[k].tg){
		if(s[k].ch[0]) s[s[k].ch[0]].tg=s[k].tg;
		if(s[k].ch[1]) s[s[k].ch[1]].tg=s[k].tg;
		s[k].tg_pt=s[k].tg;s[k].tg=0;
		s[s[k].ch[0]].has_tag=s[s[k].ch[1]].has_tag=1;
	}
}
//bool dfscheck(int k){
//	if(!k) return 0;
//	bool ok=dfscheck(s[k].ch[0])|dfscheck(s[k].ch[1]);
//	if(ok&&s[k].tg) exit(1);
//	return ok|(s[k].tg>0)|(s[k].tg_pt>0);
//}
void pushtag(int k,int x1,int y1,int x2,int y2,int v){
	if(!k) return;
	if(s[k].mx[0]<x1||s[k].mn[0]>x2||s[k].mx[1]<y1||s[k].mn[1]>y2) return;
	pushdown(k);
	if(x1<=s[k].mn[0]&&s[k].mx[0]<=x2&&y1<=s[k].mn[1]&&s[k].mx[1]<=y2){
		clrtag(k);B.add(v,s[k].sum);s[k].tg=v;pushup(k);return;
	}
	if(x1<=s[k].val[0]&&s[k].val[0]<=x2&&y1<=s[k].val[1]&&s[k].val[1]<=y2){
		if(s[k].tg_pt) /*printf("!!! %d ",s[k].val[0]),*/B.add(s[k].tg_pt,-s[k].val.v);
		s[k].tg_pt=v;/*printf("!!! %d ",s[k].val[0]);*/B.add(s[k].tg_pt,s[k].val.v);
	} pushtag(s[k].ch[0],x1,y1,x2,y2,v);pushtag(s[k].ch[1],x1,y1,x2,y2,v);
	pushup(k);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&p[i][1],&p[i].v),p[i][0]=i;
	scanf("%d",&m);for(int i=1;i<=m;i++) scanf("%d%d%d%d",&b[i].x1,&b[i].x2,&b[i].y1,&b[i].y2);//pay attention to the sequence of input
	scanf("%d",&qu);for(int i=1,l,r;i<=qu;i++) scanf("%d%d",&l,&r),qv[r].pb(mp(l,i));
	build(rt,1,n);B.init();
	for(int i=1;i<=m;i++){
		pushtag(rt,b[i].x1,b[i].y1,b[i].x2,b[i].y2,i);
		for(pii p:qv[i]) res[p.se]=B.query(p.fi,i);
//		dfscheck(rt);
	}
	for(int i=1;i<=qu;i++) printf("%lld\n",res[i]);
	return 0;
}

欸,貌似再下一个题号就是我们月赛的题了呢

posted @ 2021-09-21 23:12  tzc_wk  阅读(46)  评论(0编辑  收藏  举报