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;
}