[NOIP2016]天天爱跑步(桶)

六月ゝ 毕业季﹏ 提交于 2019-11-27 16:32:50

题意

给定一颗边权为1的树,点权为v,给定m条从s到t的路径,对于每个点,求\(ans_i=\Sigma_{j=1}^m [dist(s_j,i)==v[i]]\)

思路

有两种可能的做法,一种是把路径全加进去,再每一个点求\(ans\),另一种是一条一条加路径,每次求贡献。而这道题用的是第一种

对于每一条路径,将\(s->t\)的路径拆分为\(s->lca->t\)\(s->lca\)为左路径,\(lca->t\)为右路径,对于左路径上的点\(i\),左路径对它有贡献当且仅当\(dep[s]-dep[i]==v[i]\),将\(i\)项移动到一边,就有\(dep[s]==dep[i]+v[i]\),这样就可以把路径全部加进去再求\(i\)点的贡献。右路径同理有\(dep[s]-2*dep[lca]==v[i]-dep[i]\)

\(dfs\)整颗树,从下向上回溯时求\(ans\),对于回溯时遇到的点\(i\),加入以它为\(s/t\)的左右路径,求一次\(ans\),退出时减去以它为\(lca\)的左右路径即可,然后就发现过不了样例

(假设当前在以\(u\)为根的子树中\(dfs\))因为在\(u\)的某一珂子树\(v\)中的时候,\(u\)的其他子树里面的路径也被记录下来了,这时就会导致一条路径对不在它上面的点做出贡献。
解决方法:因为求的是满足上面两个约束的路径条数,那么在递归进入\(v\)子树加入里面的路径之前,先减去此时满足约束条件的路径条数,这样就保证了做出贡献的都是\(v\)子树里面出发的路径

另外,如果\(i\)是路径的\(lca\)的话可能被计算两次(左右路径各一次),所以要减1,即当满足\(v[lca]==dep[s]-dep[lca]\)的时候减1

Code:

#include<bits/stdc++.h>
#define N 900005
#define M 300005
using namespace std;
const int temp = 300000;
int n,m;
int dep[M],w[M],fa[M][18],ans[M];
int lsum[N],rsum[N];//桶 
vector<int> L[M],R[M];//lca在上面,出 
vector<int> l[M],r[M];//s,t在下面,入 
struct Edge
{
    int next,to;
}edge[M<<1];int head[M],cnt=1;
void add_edge(int from,int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}
template <class T>
void read(T &x)
{
    char c;int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
void dfs(int rt)
{
    dep[rt]=dep[fa[rt][0]]+1;
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[rt][0]) continue;
        fa[v][0]=rt;
        for(int i=1;i<18;++i) fa[v][i]=fa[fa[v][i-1]][i-1];
        dfs(v);
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=17;i>=0;--i) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if(x==y) return x;
    for(int i=17;i>=0;--i) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void DFS(int rt)
{
    ans[rt]-=lsum[dep[rt]+w[rt]+temp]+rsum[w[rt]-dep[rt]+temp];
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[rt][0]) continue;
        DFS(v);
    }
    //加入rt
    for(int i=0;i<(int)l[rt].size();++i)
    {
        int val=l[rt][i]+temp;
        ++lsum[val];
    }
    for(int i=0;i<(int)r[rt].size();++i)
    {
        int val=r[rt][i]+temp;
        ++rsum[val];
    }
    ans[rt]+=lsum[dep[rt]+w[rt]+temp]+rsum[w[rt]-dep[rt]+temp];
    //删除以rt为lca的链 
    for(int i=0;i<(int)L[rt].size();++i)
    {
        int val=L[rt][i]+temp;
        --lsum[val];
    }
    for(int i=0;i<(int)R[rt].size();++i)
    {
        int val=R[rt][i]+temp;
        --rsum[val];
    }   
}
int main()
{
    read(n);read(m);
    for(int i=1;i<n;++i)
    {
        int x,y;
        read(x);read(y);
        add_edge(x,y);
        add_edge(y,x);
    }
    dfs(1);
    for(int i=1;i<=n;++i) read(w[i]);
    for(int i=1;i<=m;++i)
    {
        int S,T;
        read(S);read(T);
        int lc=lca(S,T);
        if(w[lc]==dep[S]-dep[lc]) ans[lc]--;
        L[lc].push_back(dep[S]);
        R[lc].push_back(dep[S]-2*dep[lc]);
        l[S].push_back(dep[S]);
        r[T].push_back(dep[S]-2*dep[lc]);
    }
    DFS(1);
    for(int i=1;i<=n;++i) printf("%d ",ans[i]); 
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!