3757: 苹果树 树上莫队 位运算技巧

来源:互联网 发布:台湾驱动透视源码 编辑:程序博客网 时间:2024/04/30 04:49

引用Vfleaking神犇[WC2013]糖果公园的一些题解。
本题也可以如此解决,神奇的莫队:

上一次询问用(curV, curU, curTi)表示,并且我们还保留了
visited[v]:v节点在不在curV到curU的路径上
col[v]:v节点的颜色(原题好像是糖果来着?我就叫颜色了。)
occur[c]:颜色c在curV到curU的路径上出现的次数
outcome:当前的答案。
这些信息。
现在又来了一个神奇的询问!(targetV, targetU, targetTi)
那么将curV 移动到targetV去,curU移动到targetU, 时间curTi移动到targetTi上去,然后再回答询问。
先说时间的移动好了,这个比较简单。
预处理出来每个修改在修改之前的颜色。(修改之后的颜色是输入)
如果targetTi > curTi
那么不停地curTi++,执行当前修改。
如果targetTi <= curTi
那么不停地curTi++,撤销当前修改,利用刚才的预处理。
给一个节点改变颜色的方式是,如果visited了,就XXXXXX,如果没visited,就XXXXXX。讨论一下就行了囧。
然后是节点的移动。
好像巨纠结啊囧!!!
我只会sb方法,求神犇赐教。
用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))上的结点,将它们的存在性取反即可。
我之前说的visited[]、occur[]、outcome的定义并不方便,因为有个lca搀和。
干脆用这仨记录集合T(curV, curU)的情况,更加方便处理。
我觉得还是上代码比较有亲切感。
verXor函数的作用是将一个结点的存在性取反。
val 即题目中的V。
sumCoe是题目中的W的前缀和。
father[v][0]是结点v的父亲。
depth[v]是结点的深度。

T_T为什么我写的这么慢。。。

#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>using namespace std;int n,m,cnt,top,ans,scc,ind,root,block;int head[50005],dfn[50005],s[50005],c[50005],pos[50005],stack[50005],deep[50005],fa[50005][17];int next[100005],list[100005];bool vis[50005];struct node {int u,v,a,b,id,ans;} a[100005];inline int read(){    int a=0,f=1; char c=getchar();    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}    return a*f;}inline void insert(int x,int y){    next[++cnt]=head[x];    head[x]=cnt;    list[cnt]=y;}inline bool cmp(node a,node b){    return pos[a.u]==pos[b.u]?dfn[a.v]<dfn[b.v]:pos[a.u]<pos[b.u];}inline bool cmp0(node a,node b){    return a.id<b.id;}int dfs(int x){    int size=0;    dfn[x]=++ind;    for (int i=1;(1<<i)<=deep[x];i++)        fa[x][i]=fa[fa[x][i-1]][i-1];    for (int i=head[x];i;i=next[i])        if (list[i]!=fa[x][0])        {            fa[list[i]][0]=x;            deep[list[i]]=deep[x]+1;            size+=dfs(list[i]);            if (size>=block)            {                scc++;                for (int j=1;j<=size;j++) pos[stack[top--]]=scc;                size=0;            }        }    stack[++top]=x;    return size+1;}inline int lca(int x,int y){    if (deep[x]<deep[y]) swap(x,y);    int t=deep[x]-deep[y];    for (int i=0;(1<<i)<=t;i++)        if (t&(1<<i)) x=fa[x][i];    for (int i=16;~i;i--)        if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];    return x==y?x:fa[x][0];}inline void rever(int x){    if (!vis[x]) {vis[x]=1; s[c[x]]++; if (s[c[x]]==1) ans++;}    else {vis[x]=0; s[c[x]]--; if (s[c[x]]==0) ans--;}}inline void solve(int u,int v){    while (u!=v)         if (deep[u]>deep[v]) rever(u),u=fa[u][0]; else rever(v),v=fa[v][0];}int main(){    n=read(); m=read();    block=(int)(sqrt(n));    for (int i=1;i<=n;i++) c[i]=read();    for (int i=1;i<=n;i++)    {        int u=read(),v=read();        if (!u) root=v;        else if (!v) root=u;        else insert(u,v),insert(v,u);    }    dfs(root);    scc++;    while (top) pos[stack[top--]]=scc;    for (int i=1;i<=m;i++)    {        a[i].u=read(); a[i].v=read(); a[i].id=i; a[i].a=read(); a[i].b=read();        if (dfn[a[i].u]>dfn[a[i].v]) swap(a[i].u,a[i].v);    }    sort(a+1,a+m+1,cmp);    int t=lca(a[1].u,a[1].v);    solve(a[1].u,a[1].v);    rever(t);    a[1].ans=ans;    if (s[a[1].a]&&s[a[1].b]&&a[1].a!=a[1].b) a[1].ans--;    rever(t);    for (int i=2;i<=m;i++)    {        solve(a[i-1].u,a[i].u); solve(a[i-1].v,a[i].v);        int t=lca(a[i].u,a[i].v);        rever(t);        a[i].ans=ans;        if (s[a[i].a]&&s[a[i].b]&&a[i].a!=a[i].b) a[i].ans--;        rever(t);    }    sort(a+1,a+m+1,cmp0);    for (int i=1;i<=m;i++) printf("%d\n",a[i].ans);    return 0;}
0 0
原创粉丝点击