zoukankan      html  css  js  c++  java
  • NOIP2016 天天爱跑步

    4719: [Noip2016]天天爱跑步

    Time Limit: 40 Sec  Memory Limit: 512 MB
    Submit: 1457  Solved: 488
    [Submit][Status][Discuss]

    Description

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

    Input

    第一行有两个整数N和M 。其中N代表树的结点数量, 同时也是观察员的数量, M代表玩家的数量。
    接下来n-1 行每行两个整数U和V ,表示结点U 到结点V 有一条边。
    接下来一行N 个整数,其中第个整数为Wj , 表示结点出现观察员的时间。
    接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。
    对于所有的数据,保证 。
    1<=Si,Ti<=N,0<=Wj<=N
     

    Output

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

     

    Sample Input

    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 Output

    2 0 0 1 1 1

    HINT


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

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

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

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

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

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

    题目分析:

    先来分析一条链的做法,在链上路径必然只有两种情况,要么从左往右要么从右往左

    即:S <= T || S >T 两种情况

    //仅分析从左往右走的情况(对应到树上为从下往上),另一种情况类比

    那我们考虑一个点i满足什么条件时可以观察到一个玩家:w[i] = i - S 

    对于这个式子,我们可以进行移项,得到 S = i - w[i],i - w[i]对于一个观察者来说是定值

    所以其实,对于这种路线而言,会对一个观察者做出贡献的人,其实都来自于同一个起点

    也就是只有从一个特定起点出发才可能(如果一个玩家从S出发但整条路径不经过i便不贡献)对i做出贡献

    (图片来自menci的博客)

    于是,我们可以将问题进行如下转换:

    1.在区间l - r上每个点放一个数字(添加一条路径的时候,把起点加进去)

    2.查询一个点上,有多少个数字等于特定的S(即每个观察员会观察到几个玩家)

    (由于问题在一棵树上,可以树链剖分之后转换为序列)

    分析至此,第一种做法产生了:  树链剖分 + 主席树

    主席树不会怎么办!来看第二种做法

    将问题放到树上来考虑:首先来对一条路径拆分,一条u -> v的路径可以拆分成

    u -> lca (从下往上走) 和  lca -> v(从上往下走) 两部分,仍然只讲解从下往上的部分

    只考虑从下向上的路径的话,会对一个观察员 i 做出贡献的玩家必须满足如下条件:

    ①起点在 i 的子树中

    ②并且路径经过点i

    ③并且起点是上面分析中的特定的点

    (转换到树上之后,这样特定的点不再像序列上一样是一个点了,而是深度相同的一些点)

    先不考虑条件二,如果只有条件一三的话,我们只要统计这样一个东西:

    一个点的子树中,有多少条起点在特定深度的路径。条数就是观察员能观察到的玩家数量

    这个问题只要一遍dfs就可以解决(运用差分的思想)

    那条件二呢?可以考虑用全部的路径数量 - 不满足条件二的路径数量

    如何计算不满足条件二的路径数量呢?

    不难发现,起点在 i 的子树中,并且路径不经过点i 的路径,终点一定也在i的子树中

    并且起点终点的lca必定也在i的子树中,那么我们只要记录以每个点为lca的路径是哪些

    在dfs点i的子树中的点的时候,把以子树中的点为lca的路径减去即可(一个细节复杂的树形dp)

    对于另一种从上往下走的路径,类比以上即可

    至此做法二结束 lca + 树上差分 + 脑洞统计

    CODES:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstdlib>
      4 #include <cstring>
      5 #include <vector>
      6 #define max(a, b) ((a) > (b) ? (a) : (b))
      7 #define min(a, b) ((a) < (b) ? (a) : (b))
      8 #define abs(a) ((a) < 0 ? (-1 * (a)) : a)
      9 
     10 inline void read(int &x)
     11 {
     12     x = 0;char ch = getchar(), c = ch;
     13     while(ch < '0' || ch > '9')c = ch, ch = getchar();
     14     while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();
     15     if(c == '-')x = -x;
     16 }
     17 
     18 const int INF = 0x3f3f3f3f;
     19 const int MAXN = 300000 + 10;
     20 const int MAXM = 400000 + 10; 
     21 const int PIANYI = 300000;
     22 
     23 struct Edge
     24 {
     25     int u,v,next;
     26     Edge(int _u, int _v, int _next){u = _u;v = _v;next = _next;}
     27     Edge(){}
     28 }edge[MAXN << 1];
     29 int head[MAXN], cnt;
     30 inline void insert(int a, int b)
     31 {
     32     edge[++cnt] = Edge(a,b,head[a]);
     33     head[a] = cnt;
     34 }
     35 
     36 struct qEdge
     37 {
     38     int u,v,next,id;
     39     qEdge(int _u, int _v, int _next, int _id){u = _u;v = _v;next = _next;id = _id;}
     40     qEdge(){}
     41 }qedge[MAXN << 1];
     42 int qhead[MAXN], qcnt = 1;
     43 inline void qinsert(int a, int b, int id)
     44 {
     45     qedge[++qcnt] = qEdge(a,b,qhead[a],id);
     46     qhead[a] = qcnt;
     47 }
     48 
     49 int n,m,tong[2000000],ma;
     50 std::vector<int> vec[MAXN];
     51 std::vector<int> end[MAXN];
     52 
     53 struct Node
     54 {
     55     int s, t, lca, lenth;
     56 }node[MAXM];
     57 
     58 int w[MAXN],fa[MAXN],b[MAXN],deep[MAXN];
     59 
     60 int find(int x)
     61 {
     62     return x == fa[x] ? x : fa[x] = find(fa[x]);
     63 }
     64 
     65 void dfs2(int u)
     66 {
     67     b[u] = 1;
     68     for(register int pos = head[u];pos;pos = edge[pos].next)
     69     {
     70         int v = edge[pos].v;
     71         if(b[v])continue;
     72         deep[v] = deep[u] + 1;
     73         ma = max(ma, deep[v]);
     74         dfs2(v);
     75         int f1 = find(u), f2 = find(v);
     76         fa[f2] = f1;
     77     }
     78     for(register int pos = qhead[u];pos;pos = qedge[pos].next)
     79     {
     80         int v = qedge[pos].v, tmp = find(v);
     81         node[qedge[pos].id].lca = tmp;
     82         node[qedge[pos].id].lenth = deep[u] + deep[v] - 2 * deep[tmp];
     83     }
     84 }
     85 
     86 void tarjan_lca()
     87 {
     88     deep[1] = 0;
     89     dfs2(1);
     90 }
     91 
     92 int ans[MAXN],s[MAXN];
     93 //ans[i]表示i这个点能看到多少人,s[i]表示在i这个点有几个起点,t[i]表示在i这个点有几个终点 
     94 
     95 void dfs1(int u)
     96 {
     97     b[u] = 1;
     98     int tmp1;
     99     if(deep[u] + w[u] <= ma)tmp1 = tong[deep[u] + w[u]];
    100     //递归子节点 
    101     for(register int pos = head[u];pos;pos = edge[pos].next)
    102     {
    103         int v = edge[pos].v;
    104         if(b[v]) continue;
    105         dfs1(v);
    106     }
    107     tong[deep[u]] += s[u];
    108     if(deep[u] + w[u] <= ma) ans[u] += tong[deep[u] + w[u]] - tmp1; 
    109     //把以u为LCA的路径删除掉 
    110     for(register int i = vec[u].size() - 1;i >= 0;-- i)    
    111         -- tong[deep[node[vec[u][i]].s]];
    112 }
    113 
    114 void dfs3(int u)
    115 {
    116     b[u] = 1;
    117     int tmp2;
    118     tmp2 = tong[w[u] - deep[u] + PIANYI];
    119     //递归子节点 
    120     for(register int pos = head[u];pos;pos = edge[pos].next)
    121     {
    122         int v = edge[pos].v;
    123         if(b[v]) continue;
    124         dfs3(v);
    125     }
    126     for(register int i = end[u].size() - 1;i >= 0;-- i)
    127         ++ tong[node[end[u][i]].lenth - deep[u] + PIANYI];
    128     ans[u] += tong[w[u] - deep[u] + PIANYI] - tmp2; 
    129     for(register int i = vec[u].size() - 1;i >= 0;-- i)    
    130         -- tong[node[vec[u][i]].lenth - deep[node[vec[u][i]].t] + PIANYI];
    131 }
    132 
    133 int main()
    134 {
    135     read(n), read(m);
    136     for(register int i = 1;i < n;++ i)
    137     {
    138         int tmp1, tmp2;
    139         read(tmp1), read(tmp2);
    140         insert(tmp1, tmp2);
    141         insert(tmp2, tmp1); 
    142     }
    143     for(register int i = 1;i <= n;++ i)
    144         read(w[i]), fa[i] = i;
    145     for(register int i = 1;i <= m;++ i)
    146     {
    147         read(node[i].s), read(node[i].t);
    148         qinsert(node[i].s, node[i].t, i);
    149         qinsert(node[i].t, node[i].s, i); 
    150         ++ s[node[i].s]; 
    151     } 
    152     tarjan_lca();
    153     for(register int i = 1;i <= n;++ i)
    154         vec[node[i].lca].push_back(i), end[node[i].t].push_back(i);
    155     memset(b, 0, sizeof(b));
    156     dfs1(1);
    157     memset(b, 0, sizeof(b));
    158     dfs3(1);
    159     for(register int i = 1;i <= m;++ i)
    160         if(deep[node[i].s] == deep[node[i].lca] + w[node[i].lca])
    161             -- ans[node[i].lca];
    162     for(register int i = 1;i < n;++ i)
    163         printf("%d ", ans[i]);
    164     printf("%d", ans[n]);
    165     return 0;
    166 }
    View Code
  • 相关阅读:
    分析模式的位置
    SAP .Net Connector for C#
    NETBPM开源工作流讲座
    BW处理链的时间为什么会每天推迟2秒钟?
    如何在SubScreen中取得上一screen中的值
    flash弹出窗口被ie屏蔽的解决方法
    用Eclipse 开发Flex (配置安装Flex插件)
    rtmp和http方式在播放flv方面的各自优势和劣势
    FMS4 P2P直播解决方案
    [AS3]URLLoader+URLRequest+JPGEncoder实现BitmapData图片数据保存
  • 原文地址:https://www.cnblogs.com/illyaillyasviel/p/7767160.html
Copyright © 2011-2022 走看看