luogu1600天天爱跑步

9 篇文章 0 订阅

http://www.elijahqi.win/archives/1486
题目描述

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

这个游戏的地图可以看作一一棵包含

n
n个结点和

n-1
n−1条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从

1
1到

n
n的连续正整数。

现在有

m
m个玩家,第

i
i个玩家的起点为

S_i
Si​,终点为

T_i
Ti​ 。每天打卡任务开始时,所有玩家在第

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

小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点

j
j的观察员会选择在第

W_j
Wj​秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第

W_j
Wj​秒也理到达了结点

j
j 。 小C想知道每个观察员会观察到多少人?

注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点

j
j作为终点的玩家: 若他在第

W_j
Wj​秒前到达终点,则在结点

j
j的观察员不能观察到该玩家;若他正好在第

W_j
Wj​秒到达终点,则在结点

j
j的观察员可以观察到这个玩家。

输入输出格式

输入格式:
第一行有两个整数

n
n和

m
m 。其中

n
n代表树的结点数量, 同时也是观察员的数量,

m
m代表玩家的数量。

接下来

n- 1
n−1行每行两个整数

u
u和

v
v,表示结点

u
u到结点

v
v有一条边。

接下来一行

n
n个整数,其中第

j
j个整数为

W_j
Wj​ , 表示结点

j
j出现观察员的时间。

接下来

m
m行,每行两个整数

S_i
Si​,和

T_i
Ti​,表示一个玩家的起点和终点。

对于所有的数据,保证

1\leq S_i,T_i\leq n, 0\leq W_j\leq n
1≤Si​,Ti​≤n,0≤Wj​≤n 。

输出格式:
输出1行

n
n个整数,第

j
j个整数表示结点

j
j的观察员可以观察到多少人。

输入输出样例

输入样例#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号点,

W_i=0
Wi​=0,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共有2人被观察到。

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

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

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

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

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

【子任务】

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

【提示】

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

在最终评测时,调用栈占用的空间大小不会有单独的限制,但在我们的工作

环境中默认会有 8 MB 的限制。 这可能会引起函数调用层数较多时, 程序发生

栈溢出崩溃。

我们可以使用一些方法修改调用栈的大小限制。 例如, 在终端中输入下列命

令 ulimit -s 1048576

此命令的意义是,将调用栈的大小限制修改为 1 GB。

例如,在选手目录建立如下 sample.cpp 或 sample.pas

将上述源代码编译为可执行文件 sample 后,可以在终端中运行如下命令运

行该程序

./sample

如果在没有使用命令“ ulimit -s 1048576”的情况下运行该程序, sample

会因为栈溢出而崩溃; 如果使用了上述命令后运行该程序,该程序则不会崩溃。

特别地, 当你打开多个终端时, 它们并不会共享该命令, 你需要分别对它们

运行该命令。

请注意, 调用栈占用的空间会计入总空间占用中, 和程序其他部分占用的内

存共同受到内存限制。

非常妙的一道题啊

当作模拟赛的时候最多我只能想到45分

如果我们仍然每个人去考虑那么肯定是不可行的

那么我们考虑 几次搜索遍历全树然后得出答案

我们是不是考虑 当一个点x满足dep[x]+w[x]==dep[s]的时候这个路径就可以被这个观察员观察到

反过来 考虑 当我们从lca下到v的时候是不是满足dis[u,v]-w[x]=dep[u]-dep[x]这个点就可以观察到

我们考虑一条链的情况

针对一条链 我们从后往前做 我们要维护一个统计数组a[k]表示处理到当前节点从k出发的有多少条路径

然后我们倒序 做 每次找一下a[k+w[k]]的个数就是我答案的数量 然后每到之处 减去这个地方 桶内的值的a数组 即可

对于树的情况 如果lca正好可以观察到 那么我们要把lca上可以观察到的点-1 去除重复

这个思路主要和这个神犇学习的qwq

http://www.cnblogs.com/ljh2000-jump/p/6189053.html

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define N 330000
#define dx 300000
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while (ch<'0'||ch>'9')ch=gc();
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x;
}
struct node{
    int y,next;
}data[N<<1];
struct node1{
    int x,y,len,lca;
}qr[N];
int h[N],bin[20],fa[N][20],Log[N],n,max_d,dep[N],a[N],in[N],w[N],aa[N<<2],ans[N],num,m;
vector<int> q[N],st[N],ed[N];
void dfs(int x){
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (fa[x][0]==y) continue;dep[y]=dep[x]+1;fa[y][0]=x;
        for (int j=1;j<=Log[dep[y]];++j) fa[y][j]=fa[fa[y][j-1]][j-1];dfs(y);
    }
}
void dfs1(int x){
    int pre;if (dep[x]+w[x]<=max_d) pre=a[w[x]+dep[x]];
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;
        if (fa[x][0]==y) continue;
        dfs1(y);
    }a[dep[x]]+=in[x];
    if (dep[x]+w[x]<=max_d) ans[x]+=a[w[x]+dep[x]]-pre;
    for (int i=0;i<q[x].size();++i) a[dep[q[x][i]]]--;
}
inline int lca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    int dis=dep[x]-dep[y];
    for (int i=0;i<=Log[dis];++i) if (bin[i]&dis) x=fa[x][i];
    if (x==y) return x;
    for (int i=Log[dep[x]];~i;--i) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void dfs2(int x){
    int pre=aa[w[x]-dep[x]+dx];
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (fa[x][0]==y) continue;dfs2(y);
    }
    for (int i=0;i<st[x].size();++i) aa[st[x][i]+dx]++;//printf("%d\n",st[x][i]+dx);
    ans[x]+=aa[w[x]-dep[x]+dx]-pre;
    for (int i=0;i<ed[x].size();++i) aa[ed[x][i]+dx]--;//printf("%d\n",ed[x][i]+dx); 
}
int main(){
    freopen("1600.in","r",stdin);
    n=read();m=read();Log[0]=-1;for (int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
    for (int i=0;i<=19;++i) bin[i]=1<<i;
    for (int i=1;i<n;++i){
        int x=read(),y=read();
        data[++num].y=y;data[num].next=h[x];h[x]=num;
        data[++num].y=x;data[num].next=h[y];h[y]=num;
    }
    for (int i=1;i<=n;++i) w[i]=read();dfs(1);
    for (int i=1;i<=m;++i){
        qr[i].x=read();in[qr[i].x]++;
        qr[i].y=read();qr[i].lca=lca(qr[i].x,qr[i].y);
        q[qr[i].lca].push_back(qr[i].x);qr[i].len=dep[qr[i].x]+dep[qr[i].y]-2*dep[qr[i].lca];
    }
    for (int i=1;i<=n;++i) max_d=max(max_d,dep[i]);
    //printf("%d\n",max_d);
    //for (int i=1;i<=n;++i) printf("%d ",dep[i]);
    dfs1(1);
    for (int i=1;i<=m;++i){
        st[qr[i].y].push_back(qr[i].len-dep[qr[i].y]);
        ed[qr[i].lca].push_back(qr[i].len-dep[qr[i].y]);
    }dfs2(1);
    for (int i=1;i<=m;++i) if (dep[qr[i].x]-dep[qr[i].lca]==w[qr[i].lca]) ans[qr[i].lca]--;
    for (int i=1;i<=n;++i) printf("%d ",ans[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值