bzoj 3757 苹果树(树上莫队算法)
2016-04-01 11:50
441 查看
【题意】
有若干个询问,询问路径u,v上的颜色总数,另外有要求a,b,意为将a颜色看作b颜色。
【思路】
vfk真是神系列233。
Quote:
用S(v, u)代表 v到u的路径上的结点的集合。
用root来代表根结点,用lca(v, u)来代表v、u的最近公共祖先。
那么
S(v, u) = S(root, v) xor S(root, u) xor lca(v, u)
其中xor是集合的对称差。
简单来说就是节点出现两次消掉。
lca很讨厌,于是再定义
T(v, u) = S(root, v) xor S(root, u)
观察将curV移动到targetV前后T(curV, curU)变化:
T(curV, curU) = S(root, curV) xor S(root, curU)
T(targetV, curU) = S(root, targetV) xor S(root, curU)
取对称差:
T(curV, curU) xor T(targetV, curU)= (S(root, curV) xor S(root, curU)) xor (S(root, targetV) xor S(root, curU))
由于对称差的交换律、结合律:
T(curV, curU) xor T(targetV, curU)= S(root, curV) xor S(root, targetV)
两边同时xor T(curV, curU):
T(targetV, curU)= T(curV, curU) xor S(root, curV) xor S(root, targetV)
发现最后两项很爽……哇哈哈
T(targetV, curU)= T(curV, curU) xor T(curV, targetV)
(有公式恐惧症的不要走啊 T_T)
也就是说,更新的时候,xor T(curV, targetV)就行了。
即,对curV到targetV路径(除开lca(curV, targetV))上的结点,将它们的存在性取反即可。
from vfleaking
设目前指针处于a,b,且now为已经得到的T(curV,curU)的统计答案,此次询问为u,v,则我们使a->u,b->v,设计一个vis标记,路上取反标记并更新now,即xor T(curV,targetV),最后还要取反lca(u,v)才是一个完整的u->v路径。
【代码】
有若干个询问,询问路径u,v上的颜色总数,另外有要求a,b,意为将a颜色看作b颜色。
【思路】
vfk真是神系列233。
Quote:
用S(v, u)代表 v到u的路径上的结点的集合。
用root来代表根结点,用lca(v, u)来代表v、u的最近公共祖先。
那么
S(v, u) = S(root, v) xor S(root, u) xor lca(v, u)
其中xor是集合的对称差。
简单来说就是节点出现两次消掉。
lca很讨厌,于是再定义
T(v, u) = S(root, v) xor S(root, u)
观察将curV移动到targetV前后T(curV, curU)变化:
T(curV, curU) = S(root, curV) xor S(root, curU)
T(targetV, curU) = S(root, targetV) xor S(root, curU)
取对称差:
T(curV, curU) xor T(targetV, curU)= (S(root, curV) xor S(root, curU)) xor (S(root, targetV) xor S(root, curU))
由于对称差的交换律、结合律:
T(curV, curU) xor T(targetV, curU)= S(root, curV) xor S(root, targetV)
两边同时xor T(curV, curU):
T(targetV, curU)= T(curV, curU) xor S(root, curV) xor S(root, targetV)
发现最后两项很爽……哇哈哈
T(targetV, curU)= T(curV, curU) xor T(curV, targetV)
(有公式恐惧症的不要走啊 T_T)
也就是说,更新的时候,xor T(curV, targetV)就行了。
即,对curV到targetV路径(除开lca(curV, targetV))上的结点,将它们的存在性取反即可。
from vfleaking
设目前指针处于a,b,且now为已经得到的T(curV,curU)的统计答案,此次询问为u,v,则我们使a->u,b->v,设计一个vis标记,路上取反标记并更新now,即xor T(curV,targetV),最后还要取反lca(u,v)才是一个完整的u->v路径。
【代码】
#include<set> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) #define FOR(a,b,c) for(int a=(b);a<=(c);a++) using namespace std; typedef long long ll; const int N = 4e5+10; const int D = 21; ll read() { char c=getchar(); ll f=1,x=0; while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x*f; } struct Edge { int v,nxt; }e ; int en=1,front ; void adde(int u,int v) { e[++en]=(Edge){v,front[u]}; front[u]=en; } int n,m,B,B_cnt,now,dfsc,dfn ,ans ; int pos ,a ,cnt ,vis ,fa [D],dep ,st ,top; struct Node { int id,l,r,a,b; bool operator < (const Node& rhs) const { return pos[l]<pos[rhs.l] || (pos[l]==pos[rhs.l]&&dfn[r]<dfn[rhs.r]); } } q ; int dfs(int u) { FOR(i,1,D-1) fa[u][i]=fa[fa[u][i-1]][i-1]; int size=0; dfn[u]=++dfsc; trav(u,i) { int v=e[i].v; if(v!=fa[u][0]) { fa[v][0]=u; dep[v]=dep[u]+1; size+=dfs(v); if(size>=B) { B_cnt++; while(size--) pos[st[top--]]=B_cnt; } } } st[++top]=u; return size+1; } int lca(int u,int v) { if(dep[u]<dep[v]) swap(u,v); int t=dep[u]-dep[v]; FOR(i,0,D-1) if((1<<i)&t) u=fa[u][i]; if(u==v) return u; for(int i=D-1;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } void upd(int u) { if(!vis[u]) { vis[u]=1; now+=(++cnt[a[u]])==1; } else { vis[u]=0; now-=(--cnt[a[u]])==0; } } void work(int u,int v) { while(u!=v) { if(dep[u]<dep[v]) swap(u,v); upd(u); u=fa[u][0]; } } int main() { // freopen("in.in","r",stdin); // freopen("out.out","w",stdout); n=read(),m=read(); B=sqrt(n); FOR(i,1,n) a[i]=read(); int rt,u,v; FOR(i,1,n) { u=read(),v=read(); if(!u) rt=v; else if(!v) rt=u; else adde(u,v),adde(v,u); } dfs(rt); B_cnt++; while(top) pos[st[top--]]=B_cnt; FOR(i,1,m) { q[i].l=read(),q[i].r=read(); q[i].a=read(),q[i].b=read(); q[i].id=i; if(dfn[q[i].l]>dfn[q[i].r]) swap(q[i].l,q[i].r); } sort(q+1,q+m+1); work(q[1].l,q[1].r); int lc=lca(q[1].l,q[1].r); upd(lc); ans[q[1].id]=now; ans[q[1].id]-=(q[1].a!=q[1].b)&&(cnt[q[1].a]&&cnt[q[1].b]); upd(lc); FOR(i,2,m) { work(q[i-1].l,q[i].l); work(q[i-1].r,q[i].r); lc=lca(q[i].l,q[i].r); upd(lc); ans[q[i].id]=now; ans[q[i].id]-=(q[i].a!=q[i].b)&&(cnt[q[i].a]&&cnt[q[i].b]); upd(lc); } FOR(i,1,m) printf("%d\n",ans[i]); return 0; }
相关文章推荐
- WebKit 源码分析 -- loader
- 论Java Web应用中调优线程池的重要性
- 简单介绍Android中Activity的四种启动模式
- Redis快速入门
- LS、ML与MAP 例题
- UVA 10048 Audiophobia(Floyd变形)
- IIS经典模式和集成模式在管道模型中的不同
- ARM嵌入式Linux设备树简介及应用示例
- REST web服务-学习笔记
- 锯桌腿
- mongodb安装
- Java反射
- 如何实现通过URL超链接打开IOS的APP
- 网址
- 开始java之路
- centos系统下jenkins安装与配置
- CSS裁剪属性clip使用的实例教程
- 仿人人网侧边栏滑动效果
- 最全面的65条最常用正则表达式
- Libevent源码分析(六)--- bufferevent