一种很类似分治思想的问题解决策略及示例
大体思路引入
这种问题解决策略很类似分治,你也可以认为他就是:当你不会这个问题的全部时,先做1/2或者3/4。最后将剩下的问题集中解决,最后在合并和整理。
这种策略在这两个问题互相影响时较为有用。最经典的例子就是强制在线输入输出,接下来我们来看一个例子。
一个典型例子:[SDOI2011]染色
题目大意和正解
颜色段的定义是极长的连续相同颜色被认为是一段,例如112221由三段组成:11、222、1。
给你一棵树和每个节点的初始颜色,还有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,你要是觉得我在胡扯就轻喷,谢谢。

浙公网安备 33010602011771号