UOJ261 NOIP2016 day1 T2 天天爱跑步 (lca + 桶 )

来源:互联网 发布:mysql数据库的触发器 编辑:程序博客网 时间:2024/05/19 20:22

UOJ261 NOIP2016 day1 T2 天天爱跑步

原题地址:http://uoj.ac/problem/261

题意:
天天爱跑步的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到N的连续正整数。
现在有M个玩家,第个玩家的起点为Si ,终点为Ti 。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)
小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点J 。
小C想知道每个观察员会观察到多少人?注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。
即对于把结点J作为终点的玩家:
若他在第Wj秒前到达终点,则在结点J的观察员不能观察到该玩家;若他正好在第Wj秒到达终点,则在结点的观察员可以观察到这个玩家。

数据范围
1<=Si,Ti<=N,0<=Wj<=N
N,M<=300000

题解:
( _ (:P」∠)_ 阵亡。)
因为在树上,每个人的路径固定,可以转化为静态问题:
对于每个点,在所有经过其的玩家路径中,起点距其为w的路径有多少条。

然后是本题极其重要的一个转化:
拆链。
把一条路径拆成 S->lca 和 lca->T的两条路径。
令x为视察员所在位置
定义len=这条路径的长度
这时可以发现,x在S->lca 距 S为w[x]满足:
dep[x]+w[x]=dep[s]
x在lca->T距 S为w[x]满足:
dep[x]-w[x]=dep[T]-len
转化成对于每个点x统计过其的路径中有多少路径的S满足dep[x]+w[x]=dep[s]或T满足dep[x]-w[x]=dep[T]-len
就转化成了统计一个深度的点有多少个的问题,用一个桶来统计即可(up[]存s的dep[s],down[]存t的dep[t]-len,每到计算完x的子树回到x,答案为现在的up[dep[x]+w[x]]+down[dep[x]-w[x]]减去进入这个点前的up[dep[x]+w[x]]+down[dep[x]-w[x]])。

因此,对于每一条路径,在其S上打上dep[S]的标记ST,在其T上打上dep[T]-len的标记ED,进入一个点,存下进入这个点前的up[dep[x]+w[x]]+down[dep[x]-w[x]])后,就把它所有ST和ED的dep分别在up[]和down[]内++。

为了使统计到的满足都是经过x的路径,就要在离开x的子节点时,把lca为这个子节点的路径所带来的影响删除,那么回到x就只会统计到经过x的路径,所以还要预先在每条路径的lca打上dep[s]和dep[t]-len的标记,好在离开lca时up[dep[s]]- -,down[dep[t]-len]- -。

另外如果这个视察员恰在一条路径lca上,若此路径满足,那么他的s和t会被各计一次,因此离开x时,若dep[x]+w[x]恰等于以其为lca的路径的dep[s],ans[x]- -。

(此类树上的统计问题要极力利用DFS后得到的子树信息求解,注意去重。)

代码:

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<vector>using namespace std;const int N=300005;const int P=19;int n,m,head[N],to[2*N],nxt[2*N],up[N],down[N<<1],num=0,st[N],dep[N],anc[N][P+1],w[N],ans[N];vector<int> ed[N],mids[N],midt[N];void build(int u,int v){    num++;    to[num]=v;    nxt[num]=head[u];    head[u]=num;}void dfs(int u,int f){    anc[u][0]=f;    dep[u]=dep[f]+1;    for(int i=1;i<P;i++)    anc[u][i]=anc[anc[u][i-1]][i-1];    for(int i=head[u];i;i=nxt[i])    {        int v=to[i];        if(v==f) continue;        dfs(v,u);    }   }int lca(int u,int v){    if(dep[u]<dep[v]) swap(u,v);    int d=dep[u]-dep[v];    for(int i=0;d;d>>=1,i++)    if(d&1) u=anc[u][i];    if(u==v) return u;    for(int i=P-1;i>=0;i--)    {        if(anc[u][i]!=anc[v][i])        u=anc[u][i],v=anc[v][i];    }    return anc[u][0];}void getans(int u,int f){    int pre=up[dep[u]+w[u]]+down[dep[u]-w[u]+N];    up[dep[u]]+=st[u];    int sz=ed[u].size();    for(int i=0;i<sz;i++) down[ed[u][i]+N]++;    for(int i=head[u];i;i=nxt[i])    {        int v=to[i];        if(v==f) continue;        getans(v,u);    }    ans[u]=up[dep[u]+w[u]]+down[dep[u]-w[u]+N]-pre;    sz=mids[u].size();    for(int i=0;i<sz;i++)    {        up[mids[u][i]]--;        if(mids[u][i]==dep[u]+w[u]) ans[u]--;    }    sz=midt[u].size();    for(int i=0;i<sz;i++)    down[midt[u][i]+N]--;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<n;i++)    {        int u,v;        scanf("%d%d",&u,&v);        build(u,v); build(v,u);    }    dfs(1,1);    for(int i=1;i<=n;i++) scanf("%d",&w[i]);    while(m--)    {        int x,y;        scanf("%d%d",&x,&y);        int z=lca(x,y);        int len=dep[x]+dep[y]-2*dep[z];        st[x]++;        ed[y].push_back(dep[y]-len);        mids[z].push_back(dep[x]);        midt[z].push_back(dep[y]-len);      }    getans(1,1);    for(int i=1;i<=n;i++)    {        printf("%d",ans[i]);        if(i!=n) printf(" ");    }    return 0;}