[BZOJ3757]苹果树(树上莫队+分块)
2018-01-29 16:11
405 查看
题目:
我是超链接题解:
我们刚刚学习了一种树上分块的方法,然后对于每个询问以左端点所在的块为第一关键字,右端点的dfs序为第二关键字排序。那么如何进行区间的转移呢?我们来证明一下!【诶等等公式恐惧的朋友们不要走】
定义S(u,v)S(u,v)表示u到v路径上的节点集合,root为根节点,lca(u,v)为u,v的lca
那么
S(u,v)=S(root,u) xor S(root,v) xor lca(u,v)
xor前面接触过,是集合的【对称差】,简单来说就是节点出现两次消掉
lca是个单点,我们再定义
T(u,v)=S(root,u) xor S(root,v)
考虑将curU移动到targetU前后T(curU,curV)的变化
T(curU,curV)=S(root,curU) xor S(root,curV)
T(targetU,curV)=S(root,targetU) xor S(root,curV)
两个分别xor一下
T(curU,curV) xor T(targetU,curV)=S(root,curU) xor S(root,curV) xor S(root,targetU) xor S(root,curV)
我们消一下(交换律+结合律)
T(curU,curV) xor T(targetU,curV)=S(root,curU) xor S(root,targetU)
留下我们要求的T(targetU,curV),去掉另一个,不如xor T(curU,curV)
T(targetU,curV)=S(root,curU) xor S(root,targetU) xor T(curU,curV)
这前两项S有点眼熟?
T(targetU,curV)=T(curU,curV) xor T(curU,targetU)
哎呀这么简短了真是神清气爽啊,那么做的时候维护T,然后计算的时候在加上LCA就可以了
突然感觉不推柿子也可以看出来结论?!
代码:
#include <cmath> #include <cstdio> #include <cstring&g 1128d t; #include <iostream> #include <algorithm> using namespace std; const int sz=24; const int N=100005; int tot,sum,ans ,pos ,dfn ,point ,v ,nxt ,h ,nn,top,f [sz],mi[sz],block,cnt,stack ,num ,a ; bool vis ; struct hh{int u,v,a,b,id;}q ; int cmp(hh a,hh b){return pos[a.u]<pos[b.u] || (pos[a.u]==pos[b.u] && dfn[a.u]<dfn[b.u]);} void addline(int x,int y) { ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; } void dfs(int x,int fa) { dfn[x]=++nn; h[x]=h[fa]+1;int bottom=top; for (int i=1;i<sz;i++) if (h[x]<mi[i]) break; else f[x][i]=f[f[x][i-1]][i-1]; for (int i=point[x];i;i=nxt[i]) if (v[i]!=fa) { f[v[i]][0]=x,dfs(v[i],x); if (top-bottom>=block) { ++cnt; while (top!=bottom) pos[stack[top--]]=cnt; } } stack[++top]=x; } int lca(int x,int y) { if (h[x]<h[y]) swap(x,y); int k=h[x]-h[y]; for (int i=0;i<sz;i++) if (k&(1<<i)) x=f[x][i]; if (x==y) return x; for (int i=sz-1;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } void change(int x) { if (vis[x]) { vis[x]=0; num[a[x]]--; if (!num[a[x]]) sum--; }else { vis[x]=1; num[a[x]]++; if (num[a[x]]==1) sum++; } } void reverse(int x,int y) { while (x!=y) if (h[x]<h[y]) change(y),y=f[y][0]; else change(x),x=f[x][0]; } int main() { int n,m,x,y,root;scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) { scanf("%d%d",&x,&y); if (!x) root=y; else if (!y) root=x; else addline(x,y); } mi[0]=1;for (int i=1;i<sz;i++) mi[i]=mi[i-1]*2; block=sqrt(n);dfs(root,0); ++cnt; while (top) pos[stack[top--]]=cnt; for (int i=1;i<=m;i++) { scanf("%d%d%d%d",&q[i].u,&q[i].v,&q[i].a,&q[i].b); if (dfn[q[i].u]>dfn[q[i].v]) swap(q[i].u,q[i].v); q[i].id=i; } sort(q+1,q+m+1,cmp); int t=lca(q[1].u,q[1].v); reverse(q[1].u,q[1].v); change(t); ans[q[1].id]=sum; if (num[q[1].a] && num[q[1].b] && q[1].a!=q[1].b) ans[q[1].id]--; for (int i=2;i<=m;i++) { change(t); reverse(q[i-1].u,q[i].u); reverse(q[i-1].v,q[i].v); t=lca(q[i].u,q[i].v);change(t); ans[q[i].id]=sum; if (num[q[i].a] && num[q[i].b] && q[i].a!=q[i].b) ans[q[i].id]--; } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); }
相关文章推荐
- bzoj3757 苹果树(路径树上莫队)
- bzoj 3757 苹果树(树上莫队算法)
- [BZOJ 3757]苹果树:树上莫队
- BZOJ 3757 苹果树 树上莫队
- bzoj 3757 苹果树(树上莫队)
- bzoj 3757: 苹果树(树上莫队)
- 【树上莫队】bzoj3757 苹果树
- BZOJ-3757 苹果树 LCA 莫队算法 树分块
- BZOJ 3757 苹果树 树上莫队
- 【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)
- 【BZOJ-3757】苹果树 块状树 + 树上莫队
- bzoj 3757 树上莫队
- 【分块】【树上莫队】bzoj1086 bzoj3052
- BZOJ_P3757 苹果树(树上莫队+LCA)
- 【树上莫队】【带修莫队】【权值分块】bzoj1146 [CTSC2008]网络管理Network
- bzoj 4129: Haruna’s Breakfast (带修改树上莫队+分块)
- BZOJ3757【树上莫队算法】
- BZOJ 4129: Haruna’s Breakfast [树上莫队 分块]
- [BZOJ4129]-Haruna’s Breakfast-带修改树上莫队+分块
- 【树上莫队】【带修莫队】【权值分块】bzoj4129 Haruna’s Breakfast