天天爱跑步

天天爱跑步

(File IO): input:running.in output:running.out

Time Limits: 2s Memory Limits: 512MB

Description
Description
Input
Input
Output
Output
Sample Input
Sample Input1:

6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6

Sample Input2:

5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5

Sample Output
Sample Output1:

2 0 0 1 1 1

Sample Output2:

1 2 1 0 1

Hint
Hint
Data Constraint
Data
Tips


解题思路


以1为根遍历一遍,求出每一个点的的层数fl[i]

对于每一对s,t都会有他们的lca,则s到t的长度len为fl[s]+fl[t]-2fl[lca]我们可以分为s->lca,lca->t考虑:
Solution

对于路径s->lca的点d如图①,在第w[d]个时间点上到达需满足

wd+fld=fls

对于路径lca->t的点d如图②,在第w[d]个时间点上到达需满足
fldfllca+flsfllca=wd
||
lenflt=wdfld

搜完以i为根的子树,我们可以用两个桶分别表示:
1. now[0][x] 为i的子树中,所有能经过i的起点s,fl[s]=x的数量
2. now[1][x] 为i的子树中,所有能经过i的终点t,len-fl[t]=x的数量

则x的节点的答案为now[0][w[x]+fl[x]]-搜完i的子树前的now[0][w[x]-fl[x]]+now[1][w[x]-fl[x]]-搜完i的子树前的now[1][w[x]-fl[x]]

Codes

#include<cstring>
#include<cstdio>
#define lim 100000000
#define ad 600010 
using namespace std;
int to[ad*2],nex[ad*2],fir[ad],las[ad],w[ad],n,m,len;
int data[ad],fl[ad],num[2][ad*2],top,f[20][ad],vfir[2][ad*2],vlas[2][ad*2],vnex[2][ad*2],sum[ad],vtot[2],vv[2][ad*2];
bool bz[300000];

void link(int x,int y)
{
    top++;
    to[top]=y;nex[top]=0;
    if(fir[x])nex[las[x]]=top;else fir[x]=top;
    las[x]=top;
}

void swap(int &a,int &b){int c=a;a=b;b=c;}

void vlink(int x,int s,int v)
{
    int a,b,c,d,e,f;
    int k=++vtot[s];
    vv[s][k]=v;
    vnex[s][k]=0;
    if(vfir[s][x])vnex[s][vlas[s][x]]=k;else vfir[s][x]=k;
    vlas[s][x]=k;
    a=x;b=s;c=v;
    d=vv[s][k];
    e=vfir[s][x];
    f=vlas[s][x];
}

void work(int x)
{
    int s1=num[0][fl[x]+w[x]+ad],s2=num[1][w[x]-fl[x]+ad];
    for(int j=0;j<2;j++)
        for(int i=vfir[j][x];i;i=vnex[j][i])
        {
            int xx=vv[j][i];
            if(xx<=lim)num[j][xx]++;
        }
    for(int i=fir[x];i;i=nex[i])
    {
        if(bz[to[i]])continue;
        bz[to[i]]=1;
        work(to[i]);
    }
    for(int j=0;j<2;j++)
        for(int i=vfir[j][x];i;i=vnex[j][i])
        {
            int xx=vv[j][i];
            if(xx>lim)num[j][xx-lim]--;
        }
    sum[x]+=num[0][ad+fl[x]+w[x]]-s1;
    sum[x]+=num[1][w[x]-fl[x]+ad]-s2;
}

int main()
{
    freopen("running.in","r",stdin);
    freopen("running.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y),link(y,x);
    }
    int h=0,t=1;
    data[1]=1;
    fl[1]=0;bz[1]=1;
    while(h<t)
    {
        int now=data[++h];
        for(int i=fir[now];i;i=nex[i])
        {
            int x=to[i];
            if(!bz[x])
            {
                bz[x]=1;
                f[0][x]=now;
                fl[x]=fl[now]+1;
                data[++t]=x;
            }
        }
    }
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int j=1;j<20;j++)
        for(int i=1;i<=n;i++)
            if(fl[i]>=(1<<j))
            {
                f[j][i]=f[j-1][f[j-1][i]];
            }
    for(int i=1;i<=m;i++)
    {
        int s,t,lca,tx,ty,temp,co=0;
        scanf("%d%d",&s,&t);
        if(s==t)
        {
            if(w[s]==0)sum[s]++;
            continue;
        }
        tx=s,ty=t;
        if(fl[tx]>fl[ty])swap(tx,ty);
        temp=fl[ty]-fl[tx];
        while(temp)
        {
            if(temp&1)ty=f[co][ty];
            temp>>=1;
            co++;
        }
        if(tx==ty)
        {
            lca=tx;
        }else
        {
            for(int j=19;j+1;j--)
            {
                if((1<<j)>fl[tx])continue;
                if(f[j][ty]!=f[j][tx])
                {
                    tx=f[j][tx];
                    ty=f[j][ty];
                }
            }
            lca=f[0][tx];
        }
        len=fl[s]+fl[t]-fl[lca]*2;
        if(w[lca]==fl[s]-fl[lca])sum[lca]++;
        if(s!=lca)
        {
            vlink(s,0,ad+fl[s]);
            vlink(lca,0,ad+fl[s]+lim);
        }
        if(t!=lca)
        {
            vlink(t,1,ad+len-fl[t]);
            vlink(lca,1,ad+len-fl[t]+lim);
        }
    }
    memset(bz,0,sizeof(bz));
    bz[1]=1;
    work(1);
    for(int i=1;i<=n;i++)printf("%d ",sum[i]);
}
### 关于 '天天跑步' 的 C++ 编程竞赛题目分析 #### 题目背景与描述 在 C++ 编程竞赛中,“天天跑步”是一道经典的图论问题,通常涉及到路径查询和差分数组的应用。这类问题的核心在于如何高效处理大规模的数据输入并优化时间复杂度。根据以往的经验,在解决此类问题时,选手需要具备扎实的图论基础以及对数据结构的良好掌握能力[^1]。 #### 数据结构的选择 为了应对该类问题中的大量节点和边操作需求,推荐使用邻接表来存储图的信息。通过邻接表的方式可以显著减少空间占用,并提高访问效率。此外,还需要引入树上差分的思想来进行区间更新操作,从而实现快速求解目标路径上的特定属性值变化情况[^3]。 #### 实现方法概述 以下是基于上述理论框架的一个可能解决方案: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; vector<int> adj[MAXN]; int depth[MAXN], parent_node[MAXN][20]; // For LCA preprocessing long long diff_array[MAXN]; // Function to preprocess the tree for LCA queries using binary lifting technique. void dfs(int u, int p){ parent_node[u][0]=p; for(auto &v : adj[u]){ if(v != p){ depth[v]=depth[u]+1; dfs(v,u); } } } // Precompute all ancestor nodes up to log(n). void pre_process_lca(int n){ memset(parent_node,-1,sizeof(parent_node)); for(int j=1;(1<<j)<n;j++) { for(int i=1;i<=n;i++) { if(parent_node[i][j-1]!=-1) parent_node[i][j]=parent_node[parent_node[i][j-1]][j-1]; } } } // Query function that finds lowest common ancestor between two given vertices. int find_LCA(int a,int b){ if(depth[a]<depth[b]) swap(a,b); // Bring both elements at same level by moving higher one down appropriately via ancestors table entries indexed logarithmically. for(int k=19;k>=0 && (a!=b);k--){ if((depth[a]-pow(2,k)) >= depth[b]) a=parent_node[a][k]; } if(a==b)return a; // Now move them together until their parents become equal which will be our answer then. for(int k=19;k>=0;k--){ if(parent_node[a][k]!=-1&&parent_node[a][k]!=parent_node[b][k]){ a=parent_node[a][k]; b=parent_node[b][k]; } } return parent_node[a][0]; } // Update difference array based on path from node A -> B including endpoints themselves too ! void update_path_diffs(int start_point , int end_point ){ while(start_point !=end_point ){ diff_array[start_point]++; start_point=find_parent_of_current_level_zero_or_above_itself_if_already_at_root(start_point ); } } int main(){ ios::sync_with_stdio(false); cin.tie(NULL); int N,Q; cin>>N>>Q; for(int i=1;i<N;i++){ int x,y; cin>>x>>y; adj[x].push_back(y); adj[y].push_back(x); } dfs(1,0); pre_process_lca(N); vector<pair<int,pair<int,char>>> events(Q+1,{}); string s; getline(cin,s); for(int q=1;q<=Q;q++){ char type; int person_id,distance_run_today; cin>>type>>person_id>>distance_run_today; if(type=='R'){ int current_location,last_seen_position; cin>>current_location>>last_seen_position; // Perform necessary updates here according to problem statement requirements... update_path_diffs(current_location,last_seen_position); }else{ cout<<(events[q].second.first ? "YES\n":"NO\n"); } } } ``` 此代码片段展示了如何利用深度优先搜索(DFS)、最近公共祖先(LCA)预处理技术以及路径差分技巧共同作用以解答“天天跑步”的核心部分——即计算某个人跑过的路程覆盖了多少个指定位置点的问题。 #### 总结 针对 “天天跑步” 类型的题目,重点在于理解其背后的数学模型转换过程,即将实际场景抽象成为易于计算机处理的形式;同时熟练运用各种高级数据结构如树状数组、线段树或者本案例提到的树形差分等工具完成最终的目标达成。持续不断地练习类似难度级别的习题有助于加深对该领域知识点的理解程度[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值