[Codeforces]852I Dating

  第一次发Codeforces上的题解呢,小C在经过各项权衡之后最终决定打破强迫症而贴一下这个模板。

 

Description

  给定一棵n个结点的树,树上每个结点要么是男孩要么是女孩,每个男孩或女孩都有一个喜欢的数字。m次询问,每次询问两个点,询问这两个点的最短路径之间能找到多少对男女,满足他们喜欢的数字相同。

Input

  第一行一个整数n,第二行n个整数表示每个结点是男孩还是女孩,第三行n个整数表示每个结点喜欢的数字fi。接下来n-1行每行表示一条无向边x,y。第n+3行一个整数m。接下来m行每行表示一个询问a,b。

Output

  共m行,每行对于每次询问输出题目所要求的答案。

Sample Input

  7
  1 0 0 1 0 1 0
  9 2 9 2 2 9 9
  2 6
  1 2
  4 2
  6 5
  3 6
  7 4
  2
  1 3
  7 5

Sample Output

  2
  3

HINT

  1 ≤ n,m≤ 105,1 ≤ fi ≤ 109,1 ≤ x,y,a,b ≤ n。

 

Solution

  “sb树上莫队。”——小D原话。

  碰到这种维护路径上数字个数的询问,我们根本就没有想在线做的欲望,然后我们就有了树上莫队。

  最普通的莫队是在数列上做的。所以树上莫队有两种做法——一种是把莫队做法搞到树上,另一种是把树搞成一个数列。

  我们选择后者。

  如果只是询问子树的话,在树上搞和在数列上搞本质上是相同的,因为一棵子树就代表dfs序上的一段区间。

  但如果是询问路径的话,似乎就有点难办,这时我们只要想一想有什么是把路径转成一段区间的方法。

  对了!就是括号序列!(不知道括号序列的可以看一看小C的另一篇博客

  设节点x的dfs的开头序为begin[x],结尾序为end[x](其实就是在括号序列中左括号和右括号的位置)。

  假设begin[x]<begin[y],这时候路径分为两种情况(继续看下去你就知道为什么要这么分):一种是x为y的祖先,另一种是互相不为祖先。

  对于前一种情况,我们只要统计区间[begin[x],begin[y]]里的信息;对于后一种情况,我们只要统计区间[end[x],begin[y]]里的信息。

  我们发现x~y这条路径上的点在区间中只被统计了一遍,而其他点被统计了0遍或2遍。

  依靠这个性质脑补一下就能写出代码啦!

  需要注意的是在第二种情况下,x和y的lca的信息会被遗漏计算。

  因为必定有begin[lca]<begin[x]且end[lca]>end[y]。

  所以在计算第二种情况时,最后的答案要加上lca对答案的影响。

  发生这种情况的原理就是括号序列的定义,每一个括号对应树上的一条有向边。

  begin[x]即左括号代表从x的父亲走到x的边,end[x]即右括号代表从x走到x的父亲的边。

  所以第二种情况中点数会比边数多1,因为begin[lca]和end[lca]都不属于x~y的路径。

  当然如果题目给的是边权而不是点权,就不用考虑这个问题啦!

 

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define MN 200005
#define MK 505
#define MS 19
using namespace std;
struct edge{int nex,to;}e[MN];
struct meg{int ld,rd,ldk,pos,lca;}b[MN];
ll ans[MN],sm;
int dfbg[MN],dfed[MN],pos[MN],hr[MN],a[MN],lb[MN],dep[MN],sum[MN][2],fa[MS][MN];
int dfn,n,m,bin,pin;
bool u[MN],sx[MN];

inline void ins(int x,int y) {e[++pin]=(edge){hr[x],y}; hr[x]=pin;}

inline int read()
{
    int n=0,f=1; char c=getchar();
    while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
    return n*f;
}

void dfs(int x,int fat,int depth)
{
    dfbg[x]=++dfn; pos[dfn]=x;
    fa[0][x]=fat; dep[x]=depth;
    for (register int i=hr[x];i;i=e[i].nex)
        if (e[i].to!=fat) dfs(e[i].to,x,depth+1);
    dfed[x]=++dfn; pos[dfn]=x;
}

bool cmp(const meg& a,const meg& b) {return a.ldk<b.ldk || a.ldk==b.ldk && a.rd<b.rd;}
inline bool bel(int x,int y) {return dfbg[x]>dfbg[y]&&dfed[x]<dfed[y];}

inline void work(int x)
{
    u[pos[x]]^=1;
    if (u[pos[x]]) ++sum[a[pos[x]]][sx[pos[x]]],sm+=sum[a[pos[x]]][sx[pos[x]]^1];
    else --sum[a[pos[x]]][sx[pos[x]]],sm-=sum[a[pos[x]]][sx[pos[x]]^1];
}

ll cal(int x,int y,int z)
{
    register int i;
    for (i=x;i<=y;++i) work(i);
    ll lt=sm;
    lt+=sum[a[z]][sx[z]^1];
    for (i=x;i<=y;++i) work(i);
    return lt;
}

int flca(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (int k=dep[x]-dep[y],i=0;k;k>>=1,++i) if (k&1) x=fa[i][x];
    if (x==y) return x;
    for (int i=MS-1;i>=0;--i) if (fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
    return fa[0][x];
}

int main()
{
    register int i,j,jr,x,y,rim;
    n=read();
    for (i=1;i<=n;++i) sx[i]=read();
    for (i=1;i<=n;++i) a[i]=lb[i]=read();
    sort(lb+1,lb+n+1);
    bin=unique(lb+1,lb+n+1)-lb-1;
    for (i=1;i<=n;++i) a[i]=lower_bound(lb+1,lb+bin+1,a[i])-lb;
    for (i=1;i<n;++i) x=read(),y=read(),ins(x,y),ins(y,x);
    dfs(1,0,1);
    for (i=1;i<MS;++i)
        for (j=1;j<=n;++j) fa[i][j]=fa[i-1][fa[i-1][j]];
    m=read();
    for (i=1;i<=m;++i)
    {
        b[i].ld=read(); b[i].rd=read(); b[i].pos=i;
        if (dfbg[b[i].ld]>dfbg[b[i].rd]) swap(b[i].ld,b[i].rd);
        b[i].lca=flca(b[i].ld,b[i].rd); if (b[i].ld==b[i].lca) b[i].lca=0;
        if (bel(b[i].rd,b[i].ld)) b[i].ld=dfbg[b[i].ld],b[i].rd=dfbg[b[i].rd];
        else b[i].ld=dfed[b[i].ld],b[i].rd=dfbg[b[i].rd];
        b[i].ldk=(b[i].ld-1)/MK+1;
    }
    sort(b+1,b+m+1,cmp);
    for (i=j=1;i<=m;i=j)
    {
        rim=b[i].ldk*MK;
        for (;b[j].ldk==b[i].ldk&&b[j].rd<=rim;++j) ans[b[j].pos]=cal(b[j].ld,b[j].rd,b[j].lca);
        for (jr=rim+1;b[j].ldk==b[i].ldk;++j)
        {
            for (;jr<=b[j].rd;++jr) work(jr);
            ans[b[j].pos]=cal(b[j].ld,rim,b[j].lca);
        }
        for (--jr;jr>rim;--jr) work(jr);
    }
    for (i=1;i<=m;++i) printf("%lld\n",ans[i]);
}

 

Last Word

  “菜鸡”三人组打Bubble Cup时队友切掉的题。由于是模板题就拿过来做一做。

  听说还有一种树分块的做法回头聆听一下教诲。

posted @ 2017-09-05 18:17  ACMLCZH  阅读(333)  评论(0编辑  收藏  举报