noip2016天天爱跑步

时间:2020-12-06 06:20:29

题目描述

小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一一棵包含 noip2016天天爱跑步个结点和 noip2016天天爱跑步条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从noip2016天天爱跑步noip2016天天爱跑步的连续正整数。

现在有noip2016天天爱跑步个玩家,第noip2016天天爱跑步个玩家的起点为 noip2016天天爱跑步,终点为 noip2016天天爱跑步 。每天打卡任务开始时,所有玩家在第noip2016天天爱跑步秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)

小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点noip2016天天爱跑步的观察员会选择在第noip2016天天爱跑步秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第noip2016天天爱跑步秒也理到达了结点 noip2016天天爱跑步 。 小C想知道每个观察员会观察到多少人?

注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点noip2016天天爱跑步作为终点的玩家: 若他在第noip2016天天爱跑步秒重到达终点,则在结点noip2016天天爱跑步的观察员不能观察到该玩家;若他正好在第noip2016天天爱跑步秒到达终点,则在结点noip2016天天爱跑步的观察员可以观察到这个玩家。

输入输出格式

输入格式:

第一行有两个整数noip2016天天爱跑步noip2016天天爱跑步 。其中noip2016天天爱跑步代表树的结点数量, 同时也是观察员的数量, noip2016天天爱跑步代表玩家的数量。

接下来 noip2016天天爱跑步行每行两个整数noip2016天天爱跑步noip2016天天爱跑步,表示结点 noip2016天天爱跑步到结点 noip2016天天爱跑步有一条边。

接下来一行 noip2016天天爱跑步个整数,其中第noip2016天天爱跑步个整数为noip2016天天爱跑步 , 表示结点noip2016天天爱跑步出现观察员的时间。

接下来 noip2016天天爱跑步行,每行两个整数noip2016天天爱跑步,和noip2016天天爱跑步,表示一个玩家的起点和终点。

对于所有的数据,保证noip2016天天爱跑步

输出格式:

输出1行 noip2016天天爱跑步个整数,第noip2016天天爱跑步个整数表示结点noip2016天天爱跑步的观察员可以观察到多少人。

输入输出样例

输入样例#1:
6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
输出样例#1:
2 0 0 1 1 1 
输入样例#2:
5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5
输出样例#2:
1 2 1 0 1 

说明

【样例1说明】

对于1号点,noip2016天天爱跑步,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共有2人被观察到。

对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。

对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。

对于4号点,玩家1被观察到,共1人被观察到。

对于5号点,玩家1被观察到,共1人被观察到。

对于6号点,玩家3被观察到,共1人被观察到。

【子任务】

每个测试点的数据规模及特点如下表所示。 提示: 数据范围的个位上的数字可以帮助判断是哪一种数据类型。

noip2016天天爱跑步

【提示】

如果你的程序需要用到较大的栈空问 (这通常意味着需要较深层数的递归), 请务必仔细阅读选手日录下的文本当rumung:/stact.p″, 以了解在最终评测时栈空问的限制与在当前工作环境下调整栈空问限制的方法。

这道题的25分可以直接暴力得到。

对于15分链状的情况,我们可以设一个不定长数组对于每个点的观察员观察时间t在所在点向左t点打标记,向右t点打标记即可。然后对于每组起点与终点,查看起点不定长数组的所在位置是否在起点和终点之间。

对于20分起点为1的情况,以1为根,所有经过时间一定是深度,所以做一个桶即可。

对于20分终点为1的情况,同样以1为根,每个点i能观察到的路径起点u一定保证dep[u]=dep[i]-time[i];time[i]表示观察时间首先做一个桶,将起点放入,之后设一个关于深度的桶,对于每个点i的答案只需知道cnt[dep[u]+time[i]]在遍历前后的变化值即可。

80分代码:

#include<cstdio>
#include<vector>
using namespace std;
const int N=;
struct X
{
int v,f,n;
}x[N<<];
int dep[N],s,fa[N],ti[N],tong[N];
vector<int>lian[N];
void add(int u,int v)
{
x[++s].n=x[u].f;
x[x[u].f=s].v=v;
}
void dfs(int u)
{
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa[u])
dep[x[i].v]=dep[fa[x[i].v]=u]+,dfs(x[i].v);
}
int lca(int u,int v)
{
while(u!=v)
if(dep[u]>=dep[v]) u=fa[u];
else v=fa[v];
return u;
}
void ju(int u,int v,int tv)
{
int tu=;
if(!ti[u]) tong[u]++;
if(u!=v&&ti[v]==tv) tong[v]++;
while(u!=v)
if(dep[u]>=dep[v])
{
u=fa[u];
++tu;
if(tu==ti[u]&&u!=v) ++tong[u];
}
else
{
v=fa[v];
--tv;
if(tv==ti[v]&&u!=v) ++tong[v];
}
return;
}
void dfs2(int u)
{
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa[u])
fa[x[i].v]=u,dep[x[i].v]=dep[u]+,dfs2(x[i].v),tong[u]+=tong[x[i].v];
}
int main()
{
//freopen("running.in","r",stdin),freopen("running.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for(int i=;i<=n;i++) scanf("%d",&ti[i]);
if(n==)
{
for(int i=;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
++tong[v];
}
dfs2();
for(int i=;i<=n;i++)
if(dep[i]==ti[i]) printf("%d ",tong[i]);
else printf("0 ");
}
else if(n==)
{
for(int i=;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
lian[u].push_back(v);
}
for(int i=;i<=n;i++)
{
int ans=;
if(ti[i]<i)
{
int len=lian[i-ti[i]].size();
for(int j=;j<len;j++)
if(lian[i-ti[i]][j]>=i) ans++;
}
if(ti[i]+i<=n)
{
int len=lian[ti[i]+i].size();
for(int j=;j<len;j++)
if(lian[ti[i]+i][j]<i||(lian[ti[i]+i][j]==i&&ti[i])) ans++;
}
printf("%d ",ans);
}
}
else if(n<=)
{
dfs();
for(int i=;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
int t=lca(u,v),dis=dep[u]+dep[v]-*dep[t];
ju(u,v,dis);
}
for(int i=;i<=n;i++)
printf("%d ",tong[i]);
}
return ;
}

100分:

链上算法都有了,树上算法还远吗?

我们把每条路径拆成从u到lca和lca到v的两条路径。(对于原本就是链的路径特判,并且非链的lca可能会多算要减去1)

1.统计u到lca的路径:与上文中以一为终点的方法相似,对于一个点a它所能观察到的点的深度应为dep[a]+time[a],所以我们同样用桶作记录。对于一条路径我们在离开u点时给dep[u]++,

离开lca时给dep[u]--;

2.统计lca到v的路径时,只是将所改的深度变为dep[lca]*2-dep[u],因为这个值可能小于0所以加上最大值。

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=;
struct X
{
int v,f,n;
}x[N<<],y[N<<];
vector<int>ad1[N],ad2[N],de1[N],de2[N];
int dep[N],s,ti[N],to1[N],to2[N<<],n,ans[N],lc[N];
void add(int u,int v,X *t)
{
t[++s].n=t[u].f;
t[t[u].f=s].v=v;
}
int find(int a)
{
int b=a;
while(to1[b]) b=to1[b];
while(b!=a)
{
int t=to1[a];
to1[a]=b;
a=t;
}
return b;
}
void dfs1(int u,int fa)
{
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa)
{
dep[x[i].v]=dep[u]+;
dfs1(x[i].v,u);
to1[x[i].v]=u;
}
for(int i=y[u].f;i;i=y[i].n)
if(y[i].v==||dep[y[i].v]) lc[i>>]=find(y[i].v);
}
void dfs2(int u,int fa)
{ if(dep[u]+ti[u]<=n) ans[u]-=to1[dep[u]+ti[u]];
if(ti[u]<=n) ans[u]-=to2[N+dep[u]-ti[u]];
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa) dfs2(x[i].v,u);
for(int i=;i<ad2[u].size();i++) ++to2[ad2[u][i]];
if(ti[u]<=n) ans[u]+=to2[N+dep[u]-ti[u]];
for(int i=;i<de2[u].size();i++) --to2[de2[u][i]];
for(int i=;i<ad1[u].size();++i) ++to1[ad1[u][i]];
if(dep[u]+ti[u]<=n) ans[u]+=to1[dep[u]+ti[u]];
for(int i=;i<de1[u].size();++i) --to1[de1[u][i]];
}
int main()
{
int m;
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v,x);add(v,u,x);
}
for(int i=;i<=n;++i) scanf("%d",&ti[i]);
s=;
for(int i=;i<=m;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v,y);add(v,u,y);
}
dfs1(,);
memset(to1,,sizeof(to1));
for(int i=;i<=m;++i)
{
int u=y[i<<|].v,v=y[i<<].v;
if(lc[i]==v)
{
ad1[u].push_back(dep[u]);
de1[v].push_back(dep[u]);
}
else if(lc[i]==u)
{
de2[u].push_back(dep[u]+N);
ad2[v].push_back(dep[u]+N);
}
else
{
ad1[u].push_back(dep[u]);
de1[lc[i]].push_back(dep[u]);
de2[lc[i]].push_back((dep[lc[i]]<<)-dep[u]+N);
ad2[v].push_back((dep[lc[i]]<<)-dep[u]+N);
if(dep[lc[i]]+ti[lc[i]]==dep[u]) --ans[lc[i]];
}
}
dfs2(,);
for(int i=;i<=n;i++)
printf("%d ",ans[i]);
return ;
}