bzoj 3757 苹果树(树上莫队算法)

【题意】

有若干个询问,询问路径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路径。

【代码】

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 11 using namespace std;
 12
 13 typedef long long ll;
 14 const int N = 4e5+10;
 15 const int D = 21;
 16
 17 ll read() {
 18     char c=getchar();
 19     ll f=1,x=0;
 20     while(!isdigit(c)) {
 21         if(c==‘-‘) f=-1; c=getchar();
 22     }
 23     while(isdigit(c))
 24         x=x*10+c-‘0‘,c=getchar();
 25     return x*f;
 26 }
 27
 28 struct Edge {
 29     int v,nxt;
 30 }e[N];
 31 int en=1,front[N];
 32 void adde(int u,int v)
 33 {
 34     e[++en]=(Edge){v,front[u]}; front[u]=en;
 35 }
 36
 37 int n,m,B,B_cnt,now,dfsc,dfn[N],ans[N];
 38 int pos[N],a[N],cnt[N],vis[N],fa[N][D],dep[N],st[N],top;
 39
 40 struct Node
 41 {
 42     int id,l,r,a,b;
 43     bool operator < (const Node& rhs) const
 44     {
 45         return pos[l]<pos[rhs.l] || (pos[l]==pos[rhs.l]&&dfn[r]<dfn[rhs.r]);
 46     }
 47 } q[N];
 48
 49 int dfs(int u)
 50 {
 51     FOR(i,1,D-1)
 52         fa[u][i]=fa[fa[u][i-1]][i-1];
 53     int size=0;
 54     dfn[u]=++dfsc;
 55     trav(u,i)
 56     {
 57         int v=e[i].v;
 58         if(v!=fa[u][0]) {
 59             fa[v][0]=u;
 60             dep[v]=dep[u]+1;
 61             size+=dfs(v);
 62             if(size>=B) {
 63                 B_cnt++;
 64                 while(size--)
 65                     pos[st[top--]]=B_cnt;
 66             }
 67         }
 68     }
 69     st[++top]=u;
 70     return size+1;
 71 }
 72 int lca(int u,int v)
 73 {
 74     if(dep[u]<dep[v]) swap(u,v);
 75     int t=dep[u]-dep[v];
 76     FOR(i,0,D-1)
 77         if((1<<i)&t) u=fa[u][i];
 78     if(u==v) return u;
 79     for(int i=D-1;i>=0;i--)
 80         if(fa[u][i]!=fa[v][i])
 81             u=fa[u][i],v=fa[v][i];
 82     return fa[u][0];
 83 }
 84 void upd(int u)
 85 {
 86     if(!vis[u]) {
 87         vis[u]=1;
 88         now+=(++cnt[a[u]])==1;
 89     } else {
 90         vis[u]=0;
 91         now-=(--cnt[a[u]])==0;
 92     }
 93 }
 94 void work(int u,int v)
 95 {
 96     while(u!=v)
 97     {
 98         if(dep[u]<dep[v]) swap(u,v);
 99         upd(u); u=fa[u][0];
100     }
101 }
102 int main()
103 {
104 //    freopen("in.in","r",stdin);
105 //    freopen("out.out","w",stdout);
106     n=read(),m=read();
107     B=sqrt(n);
108     FOR(i,1,n) a[i]=read();
109     int rt,u,v;
110     FOR(i,1,n) {
111         u=read(),v=read();
112         if(!u) rt=v;
113         else if(!v) rt=u;
114         else adde(u,v),adde(v,u);
115     }
116     dfs(rt);
117     B_cnt++;
118     while(top) pos[st[top--]]=B_cnt;
119     FOR(i,1,m) {
120         q[i].l=read(),q[i].r=read();
121         q[i].a=read(),q[i].b=read();
122         q[i].id=i;
123         if(dfn[q[i].l]>dfn[q[i].r]) swap(q[i].l,q[i].r);
124     }
125     sort(q+1,q+m+1);
126
127     work(q[1].l,q[1].r);
128     int lc=lca(q[1].l,q[1].r);
129     upd(lc);
130     ans[q[1].id]=now;
131     ans[q[1].id]-=(q[1].a!=q[1].b)&&(cnt[q[1].a]&&cnt[q[1].b]);
132     upd(lc);
133
134     FOR(i,2,m) {
135         work(q[i-1].l,q[i].l);
136         work(q[i-1].r,q[i].r);
137         lc=lca(q[i].l,q[i].r);
138         upd(lc);
139         ans[q[i].id]=now;
140         ans[q[i].id]-=(q[i].a!=q[i].b)&&(cnt[q[i].a]&&cnt[q[i].b]);
141         upd(lc);
142     }
143
144     FOR(i,1,m)
145         printf("%d\n",ans[i]);
146     return 0;
147 }
时间: 2024-10-14 10:49:55

bzoj 3757 苹果树(树上莫队算法)的相关文章

BZOJ 3757 苹果树 树上莫队

题目大意:给出一棵树,问任意两点之间有多少种不同的颜色,一个人可能会有色盲,会将A和B当成一种颜色. 思路:比较裸的树上莫队,写出来之后,很慢,怀疑是分块的缘故,然后果断找了当年比赛的标称交上去,瞬间rk1,大概看了一眼,他好像是直接用DFS序+曼哈顿距离最小生成树搞的,为什么会比分块快? 昨天下午看到这个题之后就一直在研究树上莫队的正确姿势,然后先写了树分块,后来看了很多牛人的SPOJ COT2的题解,后来又和同学探讨了好久才弄明白. 首先先将树分块,然后把区间排序,按照第一权值为左端点所在块

【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

学习了树上莫队,树分块后对讯问的$dfs序$排序,然后就可以滑动树链处理答案了. 关于树链的滑动,只需要特殊处理一下$LCA$就行了. 在这里一条树链保留下来给后面的链来转移的$now$的为这条树链上所有点除去$LCA$的颜色种数.因为如果要考虑$LCA$情况就太多了,不如单独考虑$LCA$. 转移后加上当前链的$LCA$进行统计,然后再去掉这个$LCA$更新一下$now$值给后面的链转移. 这都是我的理解,说的有点不清楚,具体请看vfk的题解 OTZ 虽然不是这道题,但是通过这篇博客学习树上莫

【BZOJ】4358: permu 莫队算法

[题意]给定长度为n的排列,m次询问区间[L,R]的最长连续值域.n<=50000. [算法]莫队算法 [题解]考虑莫队维护增加一个数的信息:设up[x]表示数值x往上延伸的最大长度,down[x]表示数值x往下延伸的最大长度. 增加一个数x时,up[x]=up[x+1]+1,down[x]=down[x-1]+1.令t=up[x]+down[x]+1,可以用于更新答案. 同时,增加x后会影响到x所在连续区间最大数和最小数,中间的数字不会影响答案(只考虑加数),所以有: down[x+up[x]

莫队算法小结(Markdown版)

wtf,最近挖坑有点小多啊,没办法>_<容我先把糖果公园A了再来写这个吧= =看看今天能不能A掉 好吧,我承认我第二天才把糖果公园A掉>_<下面把这篇小结补上 首先众所周知的是莫队算法是要把询问先按左端点属于的块排序,再按右端点排序 复杂度就先不证了,有兴趣的同学可以自己YY下或者查阅资料 下面举几个例子详细说明 1.小Z的袜子 Description: 给定一个序列m个询问 每次询问: 区间中选两个数,两个数相等的概率 若概率为0则输出01 仔细观察发现,令x表示x这个值出现的次

莫队算法小结

唔,想有更加舒爽的阅读体验请移步http://mlz000.logdown.com/posts/252433-mo-algorithm-summary 首先众所周知的是莫队算法是要把询问先按左端点属于的块排序,再按右端点排序 复杂度就先不证了,有兴趣的同学可以自己YY下或者查阅资料 下面举几个例子详细说明 1.小Z的袜子 Description: 给定一个序列m询问 每次询问: 区间中选两个数,两个数相等的概率 若概率为则输出0/1 仔细观察发现,令x表示x个值出现的次数,则每次询问[l,r]区

bzoj 3757: 苹果树(树上莫队)

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 1327  Solved: 510 [Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个1到n之间的正整数来表示一种颜色

【BZOJ-3052】糖果公园 树上带修莫队算法

3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 883  Solved: 419[Submit][Status][Discuss] Description Input Output Sample Input Sample Output 84 131 27 84 HINT Source Solution 树上带修莫队 本质还是树上莫队,详情可以转 BZOJ-3757苹果树 但是这里需要修改,就需要一些特殊的地方

【BZOJ-3757】苹果树 块状树 + 树上莫队

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1305  Solved: 503[Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个到n之间的正整数来表示一种颜色.树上

[BZOJ 3052] [wc2013] 糖果公园 【树上莫队】

题目链接:BZOJ - 3052 题目分析 这道题就是非常经典的树上莫队了,并且是带修改的莫队. 带修改的莫队:将询问按照 左端点所在的块编号为第一关键字,右端点所在的块为第二关键字,位于第几次修改之后为第三关键字 排序. 我们将块的大小设置在 n^(2/3) ,这样一共有 n^(1/3) 个块.最后算法的总复杂度会是 n^(5/3) . 每一次如何从上一个询问转移来呢? 假设上一个询问是 (lx, ly, lt) ,这次的询问是 (x, y, t) .t 代表是在第 t 次修改操作之后. 首先