• [Noip2016]天天爱跑步


    题目描述

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

    输入格式

    第一行有两个整数N和M 。其中N代表树的结点数量, 同时也是观察员的数量, M代表玩家的数量。

    接下来n-1 行每行两个整数U和V ,表示结点U 到结点V 有一条边。

    接下来一行N 个整数,其中第个整数为Wj , 表示结点出现观察员的时间。

    接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。

    对于所有的数据,保证 1<=Si,Ti<=N,0<=Wj<=N

    输出格式

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

    img


    这题暴力分很多,所以我们可以一点点地拿。

    Si=Ti——送分的数据,直接统计每个w为0的点有多少人以它为起点即可。

    w=0——只需要判断有多少个人以当前点为起点即可。

    n,m≤1000——暴力往上走,跟着统计有多少点能观测到即可。


    上面都是送分的部分,足足给了25分。

    退化成链的部分——无非就是向左走和向右走,两者类似。设当前点为u,dep(x)表示x与链首的距离,如果要对u产生贡献,那么需要满足:

    [dep[u]+w[u]=dep[s]——往左走\ dep[u]-w[u]=dep[s]——往右走 ]

    然后记录下有哪些人以当前点为起点和哪些人以当前点为终点,开个桶来计数。

    设cntl(x)表示在所有往左走的情况中x出现的次数,cntr(x)表示在所有往右走的情况中x出现的次数,sl(u)表示往左走的起点为u的路径集合,sr(u)表示往右走的起点为u的路径的集合,s(x)表示第x条路的起点。那么若当前点为u,则记录下当前的cntl(dep(u)+w(u))和cntr(dep(u)-w(u)),然后往链的末尾递归其它点,把cntl(dep(s(sl(u))))++,cntr(dep(s(sr(u))))++,等回溯时,答案就是现在的cntl(dep(s(sl(u))))减去之前的cntl(dep(s(sl(u))))再加上现在的cntr(dep(s(sr(u))))减去之前的cntr(dep(s(sr(u))))。

    Si=1——设当前点为u,dep(x)表示x的深度,如果要对u产生贡献,那么需要满足:

    [w[u]=dep[u]{Rightarrow}dep[u]-w[u]=0=dep[s] ]

    类似于链的部分,设cnt(x)表示x出现的次数。那么若当前点为u,则记录下当前的cnt(dep(u)-w(u)),即cnt(0)。然后递归子树,把cnt(dep(s))++,等回溯时,答案就是现在的cnt(0)减去之前的cnt(0)。

    Ti=1——设当前点为u,dep(x)表示x的深度,如果要对u产生贡献,那么需要满足:

    [dep[u]+w[u]=dep[s] ]

    类似于上面的做法。


    你现在已经有80分了。而且离正解也很近了

    拓展到一般情况就成了以下的几步操作:

    1.对于每条路径,求出两个端点u,v的lca,把一条路分成u->lca和lca->v两部分。

    2.设su(x)表示s在t上方的路径中以x为起点的路径的集合,sd(x)表示s在t下方的路径中以x为起点的路径的集合,tu(x)和td(x)类似。处理出它们

    3.对于当前点u,记录下当前的cnt(dep(u)+w(u))和cnt(dep(u)-w(u)),可以列出对答案做贡献的算式:

    [dep[u]+w[u]=dep[s[sd[u]]]\ dep[u]-w[u]=dep[t[td[u]]]-dist(s[td[u]],t[td[u]])-pretime(td[u]) ]

    其中pretime(x)表示x这条路的起点是不是原来路径的lca,如果是,那么pretime(x)表示到达lca的时刻,否则为0。那么我们每次把计数的数组加1即可。最后的答案类似于之前的方式计算即可。

    4.最后由于当前的子树已经处理完,我们需要把tu(u)和su(u)的地方撤销。

    需要注意的地方:

    为了防止计数的数组超界(减成负数),建议把它开大两倍并且加上一个较大的值。

    关于路径的数组也开大两倍,因为我们会从lca的地方把路径断成两段。

    由于若当前点为lca,那么答案要剪1,因为被计算了两次

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #define maxn 300001
    #define maxm 300001
    using namespace std;
    
    struct graph{
        struct edge{
            int to,next; bool flag;
            edge(){}
            edge(const int &_to,const int &_next,const bool &_flag){ to=_to,next=_next,flag=_flag; }
        }e[maxn<<1];
        int head[maxn],k;
        inline void init(){ memset(head,-1,sizeof head); }
        inline void add(const int &u,const int &v,const bool &f){ e[k]=edge(v,head[u],f),head[u]=k++; }
    }g,q;//g为题目给的树,q为询问
    
    vector<int> su[maxn],sd[maxn],tu[maxn],td[maxn];
    int s[maxm<<1],t[maxm<<1],tot;
    int pretime[maxm<<1],dis[maxm<<1],cnts[maxn<<1],cntt[maxn<<1],Lca[maxm<<1];
    int fa[maxn],dep[maxn];
    bool vis[maxn];
    int n,m,w[maxn],ans[maxn];
    
    inline int read(){
        register int x(0),f(1); register char c(getchar());
        while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
        while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    
    /*----------------tarjan---------------*/
    int get(int x){ return x==fa[x]?x:fa[x]=get(fa[x]); }
    void lca_tarjan(int u,int pre){
        fa[u]=u,dep[u]=dep[pre]+1,vis[u]=true;
        for(register int i=g.head[u];~i;i=g.e[i].next){
            int v=g.e[i].to;
            if(v!=pre) lca_tarjan(v,u),fa[v]=u;
        }
        for(register int i=q.head[u];~i;i=q.e[i].next){
            int v=q.e[i].to; bool rev=false;
            if(!vis[v]||q.e[i].flag) continue;
            q.e[i].flag=q.e[i^1].flag=true;
            int lca=get(v);
    
            if(i&1) rev=true,swap(u,v);
            if(u==lca){
                s[++tot]=u,t[tot]=v;
                su[u].push_back(tot),td[v].push_back(tot);
                dis[tot]=dep[v]-dep[u];
            }else if(v==lca){
                s[++tot]=u,t[tot]=v;
                sd[u].push_back(tot),tu[v].push_back(tot);
                dis[tot]=dep[u]-dep[v];
            }else{
                s[++tot]=lca,t[tot]=v;
                su[lca].push_back(tot),td[v].push_back(tot);
                dis[tot]=dep[v]-dep[lca];
                pretime[tot]=dep[u]-dep[lca];
                Lca[++tot]=lca;
                s[tot]=u,t[tot]=lca;
                sd[u].push_back(tot),tu[lca].push_back(tot);
                dis[tot]=dep[u]-dep[lca];
            }
            if(rev) u=v;
        }
    }
    
    /*---------------getans---------------*/
    void dfs(int u,int pre){
        int nows=cnts[dep[u]+w[u]+maxn],nowt=cntt[dep[u]-w[u]+maxn];
        for(register int i=0;i<sd[u].size();i++) cnts[dep[s[sd[u][i]]]+maxn]++;
        for(register int i=0;i<td[u].size();i++) cntt[dep[t[td[u][i]]]-dis[td[u][i]]-pretime[td[u][i]]+maxn]++;
    
        for(register int i=g.head[u];~i;i=g.e[i].next){
            int v=g.e[i].to;
            if(v!=pre) dfs(v,u);
        }
        ans[u]=cnts[dep[u]+w[u]+maxn]-nows+cntt[dep[u]-w[u]+maxn]-nowt;
        for(register int i=0;i<tu[u].size();i++) if(Lca[tu[u][i]]==u&&dep[s[tu[u][i]]]+maxn==dep[u]+w[u]+maxn) ans[u]--;
    
        for(register int i=0;i<tu[u].size();i++) cnts[dep[s[tu[u][i]]]+maxn]--;
        for(register int i=0;i<su[u].size();i++) cntt[dep[t[su[u][i]]]-dis[su[u][i]]-pretime[su[u][i]]+maxn]--;
    }
    
    int main(){
    /*------------init---------------*/
        g.init(),q.init();
        n=read(),m=read();
        for(register int i=1;i<n;i++){
            int u=read(),v=read();
            g.add(u,v,false),g.add(v,u,false);
        }
        for(register int i=1;i<=n;i++) w[i]=read();
        for(register int i=1;i<=m;i++){
            int u=read(),v=read();
            q.add(u,v,false),q.add(v,u,false);
        }
    /*-------------------------------*/
        lca_tarjan(1,0);
        dfs(1,0);
        for(register int i=1;i<=n;i++) printf("%d ",ans[i]);puts("");
        return 0;
    }
    
  • 相关阅读:
    php转义和去掉html、php标签函数
    php命令行模式
    php开启新的进程或者线程
    防止便秘的食物
    各种米的营养价值
    select option jquery javascript
    mysql datetime、date、time、timestamp区别
    五脏之对应体液志窍时
    Html简单demo_html列表中进行编辑操作
    mysql sql语句使用技巧
  • 原文地址:https://www.cnblogs.com/akura/p/10933441.html
Copyright © 2020-2023  润新知