前言:
在序列上我们喜欢的暴力美学就是分块了,莫队算法就是其中之一.
那么我们在树上可以分块,去利用莫队算法做个漂亮的暴力吗?
可以!
要不就没有这篇文章惹
树上分块:
我们先学习一下树上如何分块!
[SCOI2005] 王室联邦
这道题目就是一道经典的树上分块
我们记录一个栈,当一个子树的个数>=块大小的时候,新建一个块,把这些点扔进这个块内.
首先,对于整个树dfs,当子树的大小大于 b 时,就将它们分在一块,同时令首都为当前这个点,容易想到:对于根,可能会剩下一些点,于是将这些点分在最后一个块里,容易发下,最后这一堆与那一块之和一定小于3b,正确性得证。
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
const int maxm=1e5+100;
int id[maxm],cap[maxm],num;
int stk[maxm],top;
int n,b;
int head[maxm],to[maxm<<1],net[maxm<<1];
int cnt;
void addedge(int u,int v)
{
++cnt;
to[cnt]=v,net[cnt]=head[u],head[u]=cnt;
}
void dfs(int x,int fa)
{
int now=top;
for(int i=head[x];i;i=net[i])
if(to[i]!=fa)
{
dfs(to[i],x);
if(top-now>=b)
{
cap[++num]=x;
while(top!=now) id[stk[top--]]=num;
}
}
stk[++top]=x;
}
int main()
{
scanf("%d%d",&n,&b);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(1,0);
while(top) id[stk[top--]]=num;
printf("%d\n",num);
for(int i=1;i<=n;i++)
printf("%d ",id[i]);
puts("");
for(int i=1;i<=num;i++)
printf("%d ",cap[i]);
puts("");
return 0;
}
莫队算法在树上分块的应用:
看一道例题.
SPOJ Count on a tree II
题目大意就是每个点有一个色,每次询问求u->v路径上不同颜色点的个数.
首先先离散化权值,分块,然后莫队统计…
好吧,怎么莫队统计啊
转移部分,可以考虑将X拨动到NX位置,那么NX向上走到LCA,X走到LCA,将路径取反即可(即存在性取反,这样走过两边的路就消掉了,因为X,Y两个节点走过相同的部分是不需要走的,也就是根节点到LCA(X,Y)的部分是不需要走的);
vis[i] 表示i节点向上的边的存在性,所以vis[LCA(x,y)] 的存在性可能为0,但它又是必须取的,所以特判一下即可。
询问排序时,要根据x,y的块来排序,这样可以让我每次拨动节点的时候,保证最多移动根号N个。
然后搞搞搞就行惹
其实离线的询问我们可以直接用Tarjan求一波LCA,肯定会比倍增快惹,然而我懒。。。
代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
const int maxm=110000;
int block_big,block_num,pos[maxm],dfn[maxm],tot;
int hash[maxm],val[maxm],f[maxm],vis[maxm];
int fa[maxm][18],deep[maxm];
int head[maxm],to[maxm<<1],net[maxm<<1],cnt;
int stk[maxm],top,ans;
int Ans[maxm];
int n,m;
struct node{
int u,v,id;
}q[maxm];
inline void addedge(int u,int v)
{
cnt++;
to[cnt]=v,net[cnt]=head[u],head[u]=cnt;
}
void dfs(int x,int fax,int dep)
{
dfn[x]=++tot,fa[x][0]=fax,deep[x]=dep;
int now=top;
for(int i=1;i<=17;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=net[i])
if(to[i]!=fax)
{
dfs(to[i],x,dep+1);
if(top-now>=block_big)
{
block_num++;
while(top!=now) pos[stk[top--]]=block_num;
}
}
stk[++top]=x;
}
inline int LCA(int u,int v)
{
if(deep[u]<deep[v]) std::swap(u,v);
int k=(deep[u]-deep[v]);
for(int i=17;~i;i--)
if((1<<i)&k) u=fa[u][i];
if(u==v) return u;
for(int i=17;~i;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
inline void Xor(int x)
{
if(vis[x]) ans-=((--f[val[x]])==0);
else ans+=((++f[val[x]])==1);
vis[x]^=1;
}
inline void update(int u,int v)
{
if(deep[u]<deep[v]) std::swap(u,v);
while(deep[u]>deep[v]) Xor(u),u=fa[u][0];
while(u!=v) Xor(u),Xor(v),u=fa[u][0],v=fa[v][0];
}
inline bool comp(node a,node b)
{
return pos[a.u]==pos[b.u]?dfn[a.v]<dfn[b.v]:pos[a.u]<pos[b.u];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&val[i]),hash[i]=val[i];
std::sort(hash+1,hash+n+1);
int t=std::unique(hash+1,hash+n+1)-hash-1;
for(int i=1;i<=n;i++)
val[i]=std::lower_bound(hash+1,hash+t+1,val[i])-hash-1;
for(int i=1,u,v;i<n;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
block_big=std::sqrt(n);
dfs(1,0,0);
block_num++;
while(top) pos[stk[top--]]=block_num;
/*for(int i=1;i<=n;i++)
printf("%d ",pos[i]);
puts("");*/
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].u,&q[i].v),q[i].id=i;
if(pos[q[i].u]>pos[q[i].v]) std::swap(q[i].u,q[i].v);
}
std::sort(q+1,q+m+1,comp);
int nowu=1,nowv=1;
for(int i=1;i<=m;i++)
{
if(nowu!=q[i].u) update(nowu,q[i].u);
if(nowv!=q[i].v) update(nowv,q[i].v);
Ans[q[i].id]=ans+((f[val[LCA(q[i].u,q[i].v)]])==0);
nowu=q[i].u,nowv=q[i].v;
//printf("%d\n",LCA(nowu,nowv));
}
for(int i=1;i<=m;i++)
printf("%d\n",Ans[i]);
return 0;
}
习题集锦:
集锦个毛啊,好像没有多少题
[WC2013]糖果公园
这个需要我们搞一下修改
在询问的结构体里新增一个时间序,表示本询问处于第几次修改,然后当前与上次时间判断一下,暴力修改就好惹,转移挺简单的
卡常数哇,所有的点答案都爆int惹(有毒
写快读,貌似块的大小设置为n^(2/3)为好,其实sqrt(n)卡一卡也可以过
代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#define ll long long
const int maxm=410000;
int head[maxm],to[maxm<<1],net[maxm<<1],cnt;
int dfn[maxm],vis[maxm],sum[maxm],deep[maxm],fa[maxm][18],c[maxm],tot;
int stk[maxm],top;
int pos[maxm];
ll w[maxm],v[maxm];
ll ans,Ans[maxm];
int n,m,Q;
int block_big,block_num;
struct node{
int u,v,timx,id;
}q[maxm];
struct Node{
int c,id;
}opt[maxm];
inline int comp(node x,node y)
{
if(pos[x.u]!=pos[y.u]) return pos[x.u]<pos[y.u];
if(pos[x.v]!=pos[y.v]) return pos[x.v]<pos[y.v];
return x.timx<y.timx;
}
inline void addedge(int u,int v)
{
cnt++;
to[cnt]=v,net[cnt]=head[u],head[u]=cnt;
}
void dfs(int x,int fax,int dep)
{
dfn[x]=++tot;
fa[x][0]=fax,deep[x]=dep;
int now=top;
for(int i=1;i<=17;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=net[i])
if(to[i]!=fax)
{
dfs(to[i],x,dep+1);
if(top-now>=block_big)
{
block_num++;
while(top!=now) pos[stk[top--]]=block_num;
}
}
stk[++top]=x;
}
inline int read1()
{
int x=0,w=1;
char ch=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*w;
}
inline ll read2()
{
ll x=0,w=1;
char ch=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*w;
}
inline int LCA(int u,int v)
{
if(deep[u]<deep[v]) std::swap(u,v);
int k=(deep[u]-deep[v]);
for(int i=17;~i;i--)
if((1<<i)&k) u=fa[u][i];
if(u==v) return u;
for(int i=17;~i;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
inline void Xor(int x)
{
if(vis[x])
{
sum[c[x]]--;
ans-=w[sum[c[x]]+1]*v[c[x]];
}
else
{
sum[c[x]]++;
ans+=w[sum[c[x]]]*v[c[x]];
}
vis[x]^=1;
}
inline void change(int t)
{
int x=opt[t].id;
if(vis[x])
{
ans-=v[c[x]]*w[sum[c[x]]--];
ans+=v[opt[t].c]*w[++sum[opt[t].c]];
}
std::swap(opt[t].c,c[x]);
}
inline void modify(int t1,int t2)
{
for(int i=t1+1;i<=t2;i++) change(i);
for(int i=t1;i>=t2+1;i--) change(i);
}
inline void update(int u,int v)
{
if(deep[u]<deep[v]) std::swap(u,v);
while(deep[u]>deep[v]) Xor(u),u=fa[u][0];
while(u!=v) Xor(u),Xor(v),u=fa[u][0],v=fa[v][0];
}
int main()
{
//freopen("park.in","r",stdin);
//freopen("park.out","w",stdout);
n=read1(),m=read1(),Q=read1();
for(int i=1;i<=m;i++)
v[i]=read2();
for(int i=1;i<=n;i++)
w[i]=read2();
for(int i=1;i<n;i++)
{
int u=read1(),v=read1();
addedge(u,v),addedge(v,u);
}
for(int i=1;i<=n;i++)
c[i]=read1();
block_big=(int)std::sqrt(n);
dfs(1,0,0);
block_num++;
while(top) pos[stk[top--]]=block_num;
int cnt1,cnt2;
cnt1=cnt2=0;
for(int i=1;i<=Q;i++)
{
int type=read1(),u=read1(),v=read1();
if(!type) opt[++cnt1]=(Node){v,u};
else q[++cnt2]=(node){u,v,cnt1,cnt2};
}
for(int i=1;i<=cnt2;i++)
if(pos[q[i].u]>pos[q[i].v]) std::swap(q[i].u,q[i].v);
std::sort(q+1,q+cnt2+1,comp);
for(int i=1;i<=cnt2;i++)
{
update(q[i-1].u,q[i].u);
update(q[i-1].v,q[i].v);
modify(q[i-1].timx,q[i].timx);
int lca=LCA(q[i].u,q[i].v);
Xor(lca),Ans[q[i].id]=ans,Xor(lca);
}
for(int i=1;i<=cnt2;i++)
printf("%lld\n",Ans[i]);
return 0;
}