zoukankan      html  css  js  c++  java
  • BZOJ4719 [Noip2016]天天爱跑步

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

     

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

     

    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

    1 2 1 0 1

    HINT

     

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

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

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

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

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

    对于6号点,玩家3被观察到,共1人被观察到。
     
     
    正解:lca+统计
    解题报告:

    25分

      考虑此时n很小,可以对于每条路径上暴力模拟,经过某个点时可以看一下当前时刻,是否跟经过的点的w相等,如果相等,则贡献加一。

    45分

      注意到测试点9-12时,保证m条路径的出发点都是1,那么我们可以考虑如果将1作为树根,那么一条路径怎样才能对于它经过的点产生贡献。不难看出对于一个点i,只有在deep[i]=w[i],才有可能有贡献。我在考场上是直接用的链剖+线段树,因为这就变成模板题了,而且n不到10w,尽管复杂度偏高,但是不易错。直接对于每条路径经过的点在线段树上增加1次经过次数,显然只有deep与w相等的点才会产生贡献。

      事实上对于S=1的情况有线性的算法,正解会详细介绍,不再赘述。

     

    60分

      注意到测试点6-8时,题目保证树退化成链。我们观察一下对于链而言,有什么特别的地方。首先要明确,此时m条路径在链上肯定是要么往左要么往右,即S<=T或者S>T。先只考虑S<=T的情况,如果对于S到T之间的点i,要产生贡献的话,肯定满足i-S=w[i],移项可得S=i-w[i]时才可以满足要求。注意到等式右边只与i本身有关,不妨设为K[i],所以题目变成了查询S到T之间K[i]等于S的i的数量。因为题目只涉及到首和尾,我们可以很容易联想到差分,即对于S打上+1标记,T打上-1标记。

      根据上述思路,我们考虑具体做法:对于每个点i,我们很容易发现只有从一个特定的点出发才有可能对i产生贡献。我们考虑维护一个统计数组A,A[k]表示的是处理到当前的结点时,从k出发的路径(而且还没有走到终点)有多少条。这样对于每个点i,我们只要查询一下所对应的A[K[i]]就可以了,根据上面的分析,这就是我们的答案了。有一点注意处理:一个点i时,我们需要把以i为起点的路径加入统计数组A,再计算这个结点的贡献,最后再把以这个结点为终点的路径从A中消除,具体可以用vector实现(上述处理顺序的必要性仔细想想就很容易想通了)。

      而对于S>T的情况完全类似,只是需要把K[i]定义为i+w[i],其余做法完全类似。

          

    100分

      题目中设计的几个档次的部分分其实暗示已经很明显了。链的做法离正解就不远了。而S=1和T=1是在告诉我们什么呢?拆路径!很容易发现,一条S到T的路径可以拆成一条S到LCA的路径和LCA到T的路径,然后对于这两条路径,一条往上,一条往下,都可以对应成链的处理方式了!

      考虑对于每条路径,先将其拆分成两条路径(为了简化对LCA在两条路径中都出现的各种情况,我们可以先就让LCA出现两次,如果最后发现LCA是有贡献的,只需-1即可),同样,我们先只考虑向上的路径。如果我们对于S在统计数组A上打上1的标记,LCA在统计数组A上打上-1的标记,那么题目转化为求一个点的子树和。考虑上述做法正确性:因为只有S到LCA路径之间的点会产生贡献,而当这个点位于路径之间时,子树和会产生1的贡献,而在S的子树中或者LCA的上方都不会产生贡献。具体实现呢?对于一个点i,产生贡献的条件是deep[S]-deep[i]=w[i],同样令K[i]=deep[i]+w[i],当我们dfs到i时查询A[k[i]]的值即为贡献。为了保证正确性,我们思考统计答案的方式和顺序。首先我们肯定是在处理完i的子树之后再来处理i(想想就知道了),然后我们需要再把以i出发的向上的路径加入统计数组,再进行查询,最后把以i为终点的路径所产生的贡献在统计数组A中消除即可。注意到我们上面维护的仅仅是一个点的深度,由于同一深度的点很多,所以我们查询的时候会发现会把不在同一子树的点统计入答案,那怎么办呢?我们考虑对于一个点要查询子树和,肯定是只要单独地考虑这一个子树的贡献,所以我们可以记录进入i时A[k[i]]的值,再在访问完i的子树之后统计答案时,看一下此时新的A[k[i]]的值,容易发现新的值减掉进入时的,才是真正的i的子树中的A[k[i]]的值。这样我们就可以避免把别的子树的答案统计进来了。

      对于向下的点做法类似,有一点复杂的地方就是等式变成了deep[T]-deep[i]=len-w[i](len为路径长度),发现如果这样做的话会出现负数,那么我们就把统计数组向右平移30w位就可以了。

      上述做法如果采用的是倍增求LCA的话,复杂度就是O(nlogn);如果用tarjan离线求LCA的话,可以做到O(n+m)。

     

     注意事项

      对于树上每个结点,统计答案时不能直接查询在统计数组中的对应的路径条数,而应该统计dfs进入i时,和访问完i的子树时的变化量。

     
     
     
      O(nlogn):
     1 //It is made by ljh2000
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <cstdio>
     6 #include <cmath>
     7 #include <algorithm>
     8 #include <ctime>
     9 #include <vector>
    10 #include <queue>
    11 #include <map>
    12 #include <set>
    13 #include <string>
    14 using namespace std;
    15 typedef long long LL;
    16 const int MAXN = 300011;
    17 const int MAXM = 600011; 
    18 int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][20],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[1000011];
    19 vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
    20 struct node{ int s,t,lca,len;}a[MAXN];
    21 inline int getint(){
    22     int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    23     if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    24 }
    25 inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
    26 inline void init(int x,int fa){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; deep[v]=deep[x]+1; init(v,x); f[v][0]=x; } }
    27 inline int lca(int x,int y){
    28     if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
    29     for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
    30     for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
    31 }
    32 
    33 inline void dfs(int x,int fa){
    34     int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
    35     for(int i=first[x];i;i=next[i]) {
    36         int v=to[i]; if(v==fa) continue;
    37         dfs(v,x);
    38     }
    39     tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
    40     for(int i=0,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
    41 }
    42 
    43 inline void DFS(int x,int fa){
    44     int now=deep[x]-w[x],cun; now+=300000; cun=num[now];
    45     for(int i=first[x];i;i=next[i]) {
    46         int v=to[i]; if(v==fa) continue;
    47         DFS(v,x);
    48     }
    49     for(int i=0,ss=ljh2[x].size();i<ss;i++) num[300000+ljh2[x][i]]++;
    50     ans[x]+=num[now]-cun;
    51     for(int i=0,ss=ljh3[x].size();i<ss;i++) num[300000+ljh3[x][i]]--;
    52 }
    53 
    54 inline void work(){  
    55     n=getint(); m=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
    56     for(int i=1;i<=n;i++) w[i]=getint(); deep[1]=1; init(1,0); for(int i=1;i<=n;i++) MAXD=max(MAXD,deep[i]);
    57     for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; 
    58     for(int i=1;i<=m;i++) {
    59         a[i].s=getint(),a[i].t=getint(),val[a[i].s]++;
    60         a[i].lca=lca(a[i].s,a[i].t),a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*2;
    61         ljh[a[i].lca].push_back(a[i].s);
    62     }
    63     dfs(1,0); 
    64     for(int i=1;i<=m;i++) {
    65         ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
    66         ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
    67     }
    68     DFS(1,0);
    69     for(int i=1;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
    70     for(int i=1;i<=n;i++) { printf("%d",ans[i]); if(i<n) printf(" "); }
    71 }
    72 
    73 int main()
    74 {
    75     work();
    76     return 0;
    77 }
     
     
      O(n+m):
     1 //It is made by ljh2000
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <cstdio>
     6 #include <cmath>
     7 #include <algorithm>
     8 #include <ctime>
     9 #include <vector>
    10 #include <queue>
    11 #include <map>
    12 #include <set>
    13 #include <string>
    14 using namespace std;
    15 typedef long long LL;
    16 const int MAXN = 300011;
    17 const int MAXM = 600011; 
    18 int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][20],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[1000011];
    19 int head[MAXN],tt[MAXM],nn[MAXM],father[MAXN],vis[MAXN];
    20 vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
    21 struct node{ int s,t,lca,len;}a[MAXN];
    22 inline int getint(){
    23     int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    24     if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    25 }
    26 inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
    27 inline void LINK(int x,int y){ nn[++ecnt]=head[x]; head[x]=ecnt; tt[ecnt]=y; }
    28 inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
    29 inline void init(int x,int fa){ 
    30     father[x]=x; vis[x]=1;
    31     for(int i=head[x];i;i=nn[i]) {
    32         int v=tt[i];
    33         if(x==a[v].s&&vis[a[v].t]) a[v].lca=find(a[v].t);
    34         if(x==a[v].t&&vis[a[v].s]) a[v].lca=find(a[v].s);
    35     }
    36     for(int i=first[x];i;i=next[i]) {
    37         int v=to[i]; if(v==fa) continue; 
    38         deep[v]=deep[x]+1; init(v,x); father[v]=x;
    39         f[v][0]=x; 
    40     }    
    41 }
    42 
    43 inline int lca(int x,int y){
    44     if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
    45     for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
    46     for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
    47 }
    48 
    49 inline void dfs(int x,int fa){
    50     int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
    51     for(int i=first[x];i;i=next[i]) {
    52         int v=to[i]; if(v==fa) continue;
    53         dfs(v,x);
    54     }
    55     tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
    56     for(int i=0,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
    57 }
    58 
    59 inline void DFS(int x,int fa){
    60     int now=deep[x]-w[x],cun; now+=300000; cun=num[now];
    61     for(int i=first[x];i;i=next[i]) {
    62         int v=to[i]; if(v==fa) continue;
    63         DFS(v,x);
    64     }
    65     for(int i=0,ss=ljh2[x].size();i<ss;i++) num[300000+ljh2[x][i]]++;
    66     ans[x]+=num[now]-cun;
    67     for(int i=0,ss=ljh3[x].size();i<ss;i++) num[300000+ljh3[x][i]]--;
    68 }
    69 
    70 inline void work(){  
    71     n=getint(); m=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
    72     for(int i=1;i<=n;i++) w[i]=getint(); ecnt=0;
    73     for(int i=1;i<=m;i++) { a[i].s=getint(),a[i].t=getint(),val[a[i].s]++; LINK(a[i].s,i); LINK(a[i].t,i);}
    74     deep[1]=1; init(1,0); for(int i=1;i<=n;i++) MAXD=max(MAXD,deep[i]);
    75     for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; 
    76     for(int i=1;i<=m;i++) {
    77         a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*2;
    78         ljh[a[i].lca].push_back(a[i].s);
    79     }
    80     dfs(1,0); 
    81     for(int i=1;i<=m;i++) {
    82         ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
    83         ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
    84     }
    85     DFS(1,0);
    86     for(int i=1;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
    87     for(int i=1;i<=n;i++) { printf("%d",ans[i]); if(i<n) printf(" "); }
    88 }
    89 
    90 int main()
    91 {
    92     work();
    93     return 0;
    94 }
  • 相关阅读:
    LeetCode 130. 被围绕的区域 (DFS)
    LeetCode 200. 岛屿数量 (BFS)
    LeetCode 200. 岛屿数量 (DFS)
    Win10下Anaconda命令行相关操作
    LeetCode 53. 最大子序和
    从尾到头打印链表
    字符串逆序
    交换俩字符串
    LeetCode 147. 对链表进行插入排序
    LeetCode 面试题 02.08. 环路检测
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6184170.html
Copyright © 2011-2022 走看看