【UOJ261 BZOJ 】天天爱跑步(线段树合并)

来源:互联网 发布:周扬青 淘宝 编辑:程序博客网 时间:2024/05/16 17:37

传送门

天天爱跑步

I Think

    题意:给出一颗n个节点的树,每个节点i上有一个仅在时刻wi进行观察的观察员。有m个人在树上跑步,他们同时在0时刻开始从起点Si跑向Ti,每秒钟跑过1条边。观察员i观察到第j个人,当且仅当第j个人在第wi秒时恰好到达了i节点。每个人到达终点的下一秒即消失。问每个观察员能够观察到多少个人。
    算法:线段树合并
    思路:考虑一条路径(S,T)对节点u可能产生的贡献:
    1)当S在u的子树内时,若deep[S]deep[u]=w[u]时,该路径对点u有贡献1,移项后:

deep[u]+w[u]=deep[S]

    2)当T在u的子树内时,若Lca=LCA(S,T),deep[S]deep[Lca]+deep[u]deep[Lca]=w[u],该路径对点u有贡献1,移项后:
deep[u]w[u]=2×deep[Lca]deep[S]

    那么计算每个点的答案,则可以建立两颗权值线段树,在树上进行DFS时,自底向上分别在对应权值线段树中插入每个起/终点的贡献deep[S],2×deep[Lca]deep[S]。一边向上走更新答案,一边合并子树的权值线段树。
    3)当S,T均在u的子树内时,若u=Lca则路径S,T才可能产生一次贡献。但是根据操作1),2),此时该路径的贡献会被计算两次,因此要删除一次以点u为LCA的路径贡献,再统计答案。统计答案之后,该路径也不再会对u的任意祖先节点产生贡献,因此删去它在权值线段树中的另一次贡献。
    实现时可用vector保存以某点为T/LCA的路径。
    注意2)操作的权值可能为负,所以左右二式均加上N+1即可。

Code

#include<cstdio>#include<vector>using namespace std;const int sm = 3e5+5;const int sn = 2400000;int N,M,tot,lim;int Rt[2][sm],cnt[2][sn],Ls[2][sn],Rs[2][sn],Lc[sm];int w[sm],S[sm],T[sm],tp,stk[sn],sum[sm],Ans[sm];//bzoj数据较强需要把stk开到比较大 否则WA//sum[i]记从i点出发的路径条数int Fa[sm][22],dep[sm],hd[sm],to[sm<<1],nxt[sm<<1];vector<int > t[sm],lc[sm];//t[i]记在i点结束的路径编号 lc[i]记以i为lca的路径编号template <typename T> void read(T &x) {    char ch=getchar();x=0;    while(ch>'9'||ch<'0') ch=getchar();    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();}void Swap(int &x,int &y) { int t=x;x=y;y=t; }void Ins(int x,int y) {    to[++tot]=y,nxt[tot]=hd[x],hd[x]=tot;    to[++tot]=x,nxt[tot]=hd[y],hd[y]=tot;}void Dfsa(int x,int fa) {    Fa[x][0]=fa,dep[x]=dep[fa]+1;    for(int i=1;i<=19;++i)        if(Fa[x][i-1]) Fa[x][i]=Fa[Fa[x][i-1]][i-1];        else break;    for(int i=hd[x];i;i=nxt[i])        if(to[i]!=fa) Dfsa(to[i],x);}int Lca(int u,int v) {    if(dep[u]!=dep[v]) {        if(dep[u]<dep[v]) Swap(u,v);        for(int i=19;i>=0;--i)            if(dep[Fa[u][i]]>dep[v])                u=Fa[u][i];        u=Fa[u][0];    }    if(u==v)return v;    for(int i=19;i>=0;--i)         if(Fa[u][i]!=Fa[v][i])            u=Fa[u][i],v=Fa[v][i];    return Fa[u][0];}void del(int &x,int k) {    stk[++tp]=x,Ls[k][x]=Rs[k][x]=cnt[k][x]=0,x=0;}int Merge(int x,int &y,int k) {    if(!(x*y))return x+y;    cnt[k][x]+=cnt[k][y];    Ls[k][x]=Merge(Ls[k][x],Ls[k][y],k);    Rs[k][x]=Merge(Rs[k][x],Rs[k][y],k);    del(y,k);    return x;}void Add(int &rt,int l,int r,int p,int val,int k) {    if(!rt) { rt = tp? stk[tp--] : ++tot;}    cnt[k][rt]+=val;    if(l==r) { if(!cnt[k][rt])del(rt,k); return; }    int m=(l+r)>>1;    if(p<=m) Add(Ls[k][rt],l,m,p,val,k);    else Add(Rs[k][rt],m+1,r,p,val,k);    if(!Ls[k][rt]&&!Rs[k][rt])del(rt,k);}int Query(int rt,int l,int r,int p,int k) {    if(!rt)return 0;    if(l==r) return cnt[k][rt];    int m=(l+r)>>1;    if(p<=m) return Query(Ls[k][rt],l,m,p,k);    else return Query(Rs[k][rt],m+1,r,p,k);}void Dfsb(int x,int fa) {    for(int i=hd[x];i;i=nxt[i]) {        if(to[i]!=fa) Dfsb(to[i],x); else continue;        Rt[0][x]=Merge(Rt[0][x],Rt[0][to[i]],0);        Rt[1][x]=Merge(Rt[1][x],Rt[1][to[i]],1);    }    if(sum[x]) Add(Rt[0][x],1,lim,dep[x],sum[x],0);    for(int i=0,p;i<t[x].size();++i) {        p=N+1+dep[S[t[x][i]]]-(dep[Lc[t[x][i]]]<<1);         Add(Rt[1][x],1,lim,p,1,1);    }    Ans[x]+=Query(Rt[0][x],1,lim,w[x]+dep[x],0);    for(int i=0,p;i<lc[x].size();++i) { //删除以点x为Lca的路径        Add(Rt[0][x],1,lim,dep[S[lc[x][i]]],-1,0);        Add(Rt[1][x],1,lim,dep[S[lc[x][i]]]-(dep[x]<<1)+N+1,-1,1);    }    Ans[x]+=Query(Rt[1][x],1,lim,w[x]-dep[x]+N+1,1);}int main() {    read(N),read(M),lim=N<<1|1;    for(int i=1,u,v;i<N;++i)         read(u),read(v),Ins(u,v);    for(int i=1;i<=N;++i) read(w[i]);    for(int i=1;i<=M;++i) {        read(S[i]),read(T[i]);        sum[S[i]]++,t[T[i]].push_back(i);    }    tot=0,Dfsa(1,0);    for(int i=1;i<=M;++i)        lc[Lc[i]=Lca(S[i],T[i])].push_back(i);    Dfsb(1,0);    printf("%d",Ans[1]);    for(int i=2;i<=N;++i)        printf(" %d",Ans[i]);    return 0;}更新一波两颗线段树节点不需区分的代码
#include<vector>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int sn = 3e5 + 19;const int sm = 35e6 + 19;int n,m,lim,tot;int w[sn],ans[sn];int to[sn<<1],nxt[sn<<1],hd[sn],dep[sn];int f[sn][20],S[sn],T[sn],lca[sn],st[sn];vector<int> tt[sn],lc[sn];void Swap(int &x,int &y) { int t = x; x = y; y = t; }void Add(int u,int v) {     to[++tot] = v, nxt[tot] = hd[u], hd[u] = tot;    to[++tot] = u, nxt[tot] = hd[v], hd[v] = tot;}#define mid ((l+r)>>1)namespace St {    int Rt[2][sn],cnt;    int val[sm],Ls[sm],Rs[sm];    void Ins(int &rt,int l,int r,int pos,int k) {        if(!rt) rt = ++cnt;        val[rt] += k;        if(l == r) return;        if(pos <= mid) Ins(Ls[rt],l,mid,pos,k);        else Ins(Rs[rt],mid+1,r,pos,k);    }    int Query(int rt,int l,int r,int pos) {        if(!rt) return 0;        if(l == r) return val[rt];        if(pos <= mid) return Query(Ls[rt],l,mid,pos);        else return Query(Rs[rt],mid+1,r,pos);     }    int Merge(int Rt,int rt) {        if(!(Rt * rt)) return Rt + rt;        val[Rt] += val[rt];        Ls[Rt] = Merge(Ls[Rt],Ls[rt]);        Rs[Rt] = Merge(Rs[Rt],Rs[rt]);        return Rt;    }}using namespace St;void Dfs1(int x,int fa) {    f[x][0] = fa, dep[x] = dep[fa]+1;    for(int i = 1; i <= 19; ++i)        if(f[x][i-1]) f[x][i] = f[f[x][i-1]][i-1];        else break;    for(int i = hd[x]; i; i = nxt[i])        if(to[i]!=fa) Dfs1(to[i],x);}int Lca(int u,int v) {    if(dep[u] < dep[v]) Swap(u,v);    for(int i = 19; i >= 0; --i)        if(dep[f[u][i]] >= dep[v])             u = f[u][i];    if(u == v) return v;    for(int i = 19; i >= 0; --i)        if(f[u][i] != f[v][i])            u = f[u][i], v = f[v][i];    return f[u][0];}void Dfs2(int x) {    for(int i = hd[x]; i; i = nxt[i])        if(to[i] != f[x][0]) {            Dfs2(to[i]);            Rt[0][x] = Merge(Rt[0][x],Rt[0][to[i]]);            Rt[1][x] = Merge(Rt[1][x],Rt[1][to[i]]);        }    if(st[x]) Ins(Rt[0][x],1,lim,dep[x],st[x]);     int loc;    for(int i = 0,num; i < tt[x].size(); ++i) {        num = tt[x][i];        loc = dep[S[num]]-(dep[lca[num]]<<1)+n+1;        Ins(Rt[1][x],1,lim,loc,1);    }    ans[x] = Query(Rt[1][x],1,lim,w[x]-dep[x]+n+1);    for(int i = 0,num; i < lc[x].size(); ++i) {        num = lc[x][i];        loc = dep[S[num]]-(dep[x]<<1)+n+1;        Ins(Rt[1][x],1,lim,loc,-1);        Ins(Rt[0][x],1,lim,dep[S[num]],-1);    }    ans[x] += Query(Rt[0][x],1,lim,dep[x]+w[x]);}int main() {    int u,v;    scanf("%d%d",&n,&m);    for(int i = 1; i < n; ++i)        scanf("%d%d",&u,&v), Add(u,v);    for(int i = 1; i <= n; ++i)        scanf("%d",&w[i]);    for(int i = 1; i <= m; ++i) {        scanf("%d%d",&S[i],&T[i]);        ++st[S[i]];        tt[T[i]].push_back(i);    }    Dfs1(1,0);    for(int i = 1; i <= m; ++i)        lc[lca[i] = Lca(S[i],T[i])].push_back(i);    lim = n<<1|1, Dfs2(1);    for(int i = 1; i <= n; ++i)        printf("%d ",ans[i]);    putchar(10);    return 0;}
原创粉丝点击