头像

zhangboyiQvQ

感谢OI还有眼前的你

一种很类似分治思想的问题解决策略及示例

大体思路引入

这种问题解决策略很类似分治,你也可以认为他就是:当你不会这个问题的全部时,先做1/2或者3/4。最后将剩下的问题集中解决,最后在合并和整理。

这种策略在这两个问题互相影响时较为有用。最经典的例子就是强制在线输入输出,接下来我们来看一个例子。

一个典型例子:[SDOI2011]染色

题目大意和正解

颜色段的定义是极长的连续相同颜色被认为是一段,例如112221由三段组成:112221

给你一棵树和每个节点的初始颜色,还有m次操作:

操作1:把节点a到节点b的路径上所有的点都染成颜色c。

操作2:查询节点a到节点b的路径上有几个颜色段。

这道题就一个很明显的树剖然后线段树维护颜色段个数,
这里就不详细讲解了。为了保留主干和精简长度,不会的可以去看下题解

为什么选这个题思路?

1.这题太经典了,学树剖必做。

2.两个问题互相影响,如果线段树不熟的话,很容易想不出来线段树部分怎么维护。

如果你不会线段树部分怎么办?(这种方法拿不到满分)

这就可以用到此策略的思路:
1.首先考虑你会什么?

你会在线用线段树完成操作1。

2.你不会什么?如何解决?

你不会用线段树在线维护颜色段。但你发现如果你不会线段树的时候,你会用分块谋求一定分数,所以你决定用分块维护颜色段个数。把序列分成\(\sqrt{n}\)块,每块\(\sqrt{n}\)个数,每一个块的左右端点记作l[i]、r[i],每个点所在块编号记作id[i],把所有块都求一个答案。如果询区间问你的话把每个块合并答案:如果说上一个块的右端点,和当前快的左端点颜色相同,答案-1就行了。

像这样:

sum表示每一块的答案,Count(L,R)表示暴力数一下从L到R有多少个颜色段。

int query(int L,int R){
	if(id[L]==id[R])return Count(L,R);
	int res=0;
	for(int i=id[L]+1;i<=id[R]-1;i++)res+=sum[i];
	res+=Count(L,r[id[L]]);
	res+=Count(l[id[R]],R);
	for(int i=id[L]+1;i<=id[R];i++)merge(res,l[i]-1,l[i]);
	return res;
}

如何合并

修改的时候总不可能每个都修改数组吧。

再开一个类似Lazy标记的数组。

每次直接修改,把这个数组的对应值修改为0。

每次块的批量修改,把这个数组的对应值修改为要修改块的值就好啦。

那么这个数组肯定是用线段树维护啦。

整体代码:
笑点解析:调了好久

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=100000+5;
vector<int>e[maxn];
int tot,fa[maxn],hson[maxn],siz[maxn],dep[maxn],top[maxn],rnk[maxn],dfn[maxn],a[maxn],A[maxn];
void dfs_build(int u,int fat){
	hson[u]=0;
	siz[hson[u]]=0;
	siz[u]=1;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(v==fat)continue;
		dep[v]=dep[u]+1;
		fa[v]=u;
		dfs_build(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[hson[u]]){
			hson[u]=v;
			siz[hson[u]]=siz[v];
		}
	}
}
void dfs(int u,int fat){
	tot++;
	dfn[u]=tot;
	rnk[tot]=u;
	if(hson[u]){
		top[hson[u]]=top[u];
		dfs(hson[u],u);
		for(int i=0;i<e[u].size();i++){
			int v=e[u][i];
			if(v==fat||v==hson[u])continue;
			top[v]=v;
			dfs(v,u);
		}		
	}
}
int n,m,x,y,z,s=1,sz,cnt;
char op;
int id[maxn],l[1005],r[1005],sum[1005],Sum[maxn<<2],lazy_SegTree[maxn<<2];
void pushup(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}
void pushdown(int rt,int len){
	if(lazy_SegTree[rt]!=0){
		lazy_SegTree[rt<<1]=lazy_SegTree[rt];
		lazy_SegTree[rt<<1|1]=lazy_SegTree[rt];
		Sum[rt<<1]=(len-(len>>1))*lazy_SegTree[rt];
		Sum[rt<<1|1]=(len>>1)*lazy_SegTree[rt];
		lazy_SegTree[rt]=0;
	}
}
void Build(int l,int r,int rt){
	lazy_SegTree[rt]=0;
	if(l==r){
		Sum[rt]=0;
		return;
	}
	int mid=(l+r)>>1;
	Build(l,mid,rt<<1);
	Build(mid+1,r,rt<<1|1);
	pushup(rt);
}
void Update(int L,int R,int c,int l,int r,int rt){
	if(L<=l&&r<=R){
		lazy_SegTree[rt]=c;
		Sum[rt]=c*(r-l+1);
		return;
	}
	pushdown(rt,r-l+1);
	int mid=(l+r)>>1;
	if(mid>=L)Update(L,R,c,l,mid,rt<<1);
	if(mid+1<=R)Update(L,R,c,mid+1,r,rt<<1|1);
	pushup(rt);
} 
int Query(int L,int R,int l,int r,int rt){
	if(L<=l&&r<=R)return Sum[rt];
	pushdown(rt,r-l+1);
	int mid=(l+r)>>1,ans=0;
	if(L<=mid)ans+=Query(L,R,l,mid,rt<<1);
	if(mid+1<=R)ans+=Query(L,R,mid+1,r,rt<<1|1);
	return ans;
}
int Count(int L,int R){
	int res=1;
	for(int i=L+1;i<=R;i++){
		int _1=Query(i,i,1,n,1),_2=Query(i-1,i-1,1,n,1);
		if(_1==0)_1=a[i];
		if(_2==0)_2=a[i-1];
		if(_1!=_2)res++;
	}
	return res;
}
void merge(int&res,int L,int R){
	int _1=Query(L,L,1,n,1),_2=Query(R,R,1,n,1);
	if(_1==0)_1=a[L];
	if(_2==0)_2=a[R];
	if(_1==_2)res--;
}
void build(){
	sz=sqrt(n);
	for(int i=1;i<=n;i++)id[i]=(i-1)/sz+1,cnt+=(id[i]!=id[i-1]);
	l[1]=1,r[1]=sz;
	for(int i=2;i<cnt;i++)l[i]=l[i-1]+sz,r[i]=r[i-1]+sz;
	l[cnt]=r[cnt-1]+1,r[cnt]=n;
	for(int i=1;i<=cnt;i++)sum[i]=Count(l[i],r[i]);
}
void update(int L,int R,int c){
	if(id[L]==id[R]){
		for(int i=L;i<=R;i++)a[i]=c,Update(i,i,0,1,n,1);
		sum[id[L]]=Count(l[id[L]],r[id[R]]);
		return;
	}
	for(int i=L;i<=r[id[L]];i++)a[i]=c,Update(i,i,0,1,n,1);
	for(int i=id[L]+1;i<=id[R]-1;i++)sum[i]=1,Update(l[i],r[i],c,1,n,1);
	for(int i=l[id[R]];i<=R;i++)a[i]=c,Update(i,i,0,1,n,1);
	sum[id[L]]=Count(l[id[L]],r[id[L]]);
	sum[id[R]]=Count(l[id[R]],r[id[R]]);
}
int query(int L,int R){
	if(id[L]==id[R])return Count(L,R);
	int res=0;
	for(int i=id[L]+1;i<=id[R]-1;i++)res+=sum[i];
	res+=Count(L,r[id[L]]);
	res+=Count(l[id[R]],R);
	for(int i=id[L]+1;i<=id[R];i++)merge(res,l[i]-1,l[i]);
	return res;
}
void op1(int u,int v,int c){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]){
			update(dfn[top[v]],dfn[v],c);
			v=fa[top[v]];
		}else{
			update(dfn[top[u]],dfn[u],c);
			u=fa[top[u]];
		}
	}
	if(dep[u]>dep[v])swap(u,v);
	update(dfn[u],dfn[v],c);
}
int op2(int u,int v){
	int res=0,lastu=0,lastv=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]){
			if(lastv!=0)merge(res,lastv,dfn[v])lastv=0;
			res+=query(dfn[top[v]],dfn[v]);
			lastv=dfn[top[v]];
			v=fa[top[v]];
		}else{
			if(lastu!=0)merge(res,lastu,dfn[u])lastu=0;
			res+=query(dfn[top[u]],dfn[u]);
			lastu=dfn[top[u]];
			u=fa[top[u]];
		}
	}
	if(lastu!=0)merge(res,lastu,dfn[u]);
	if(lastv!=0)merge(res,lastv,dfn[v]);
	if(dep[u]>dep[v])swap(u,v);
	res+=query(dfn[u],dfn[v]);
	return res;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	fa[s]=s;
	dep[s]=1;
	dfs_build(s,0);
	top[s]=s;
	dfs(s,0);
	for(int i=1;i<=n;i++)A[dfn[i]]=a[i];
	for(int i=1;i<=n;i++)a[i]=A[i];
	build();
	Build(1,n,1);
	while(m--){
		cin>>op;
		if(op=='C'){
			cin>>x>>y>>z;
			op1(x,y,z);
		}
		if(op=='Q'){
			cin>>x>>y;
			cout<<op2(x,y)<<'\n';
		}
	}
	return 0;
}

这个做法显然过不了,O(\(n*logn*\sqrt{n}*log\sqrt{n}\))再加上常数巨大,我在luogu上提交是35TLE,到这里这篇文章就结束了,感谢您看到这里。

写在后面

我就一六年级xxs,你要是觉得我在胡扯就轻喷,谢谢。

posted @ 2026-02-06 10:09  zhangboyiQvQ  阅读(1)  评论(0)    收藏  举报