NOIP2016 Day1 T2天天爱跑步

Posted $mathit{AlphaINF}$

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2016 Day1 T2天天爱跑步相关的知识,希望对你有一定的参考价值。

题目传送门:https://www.luogu.org/problemnew/show/P1600

感觉这两天在处理边界问题上有点神志不清......为了从80的暴力变成100,花了整整一个下午+一个晚上的时间(还好最后还是搞了出来)

题目大意:给你一棵树N个点的无根树,有M个人要从Si走到Ti,行走速度为每秒一条边。对于树上任意节点i,求出所有经过该点时行走时间恰好为Wi的路径数量。且这M个人到达终点后下一秒会立即消失。

先来说说暴力,写得妙的话,这题暴力可以拿80分(是不是很良心??)

这种题目考场上最好还是打暴力

25分(1到5号点):直接对于所有的任务,模拟从S跑到T,随后直接统计答案即可。

技术分享
 1 bool dfs(int x,int fa,int t,int dep){
 2     if(x==t){
 3         if(dep==w[x]) ans[x]++;
 4         return 1;
 5     }
 6     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
 7         if(dfs(e[i].u,x,t,dep+1)){
 8             if(dep==w[x]) ans[x]++;
 9             return 1;
10         }
11     }
12     return 0;
13 }
25分暴力

15分(6,7,8号点):我们可以将这M个人分为两类(Si>Ti和Si≤Ti),对于一个点i,我们通过二分统计Sj==i-W[i]和Sj==W[i]-i的数量,随后直接输出即可。

技术分享
 1         if(op==4){
 2         sort(a+1,a+m+1);
 3         int na=0,nb=0;
 4         for(int i=1;i<=n;i++){
 5             if(a[i].s<=a[i].t) a[++na]=a[i];
 6             else b[++nb]=a[i];
 7         }
 8         a[na+1]=st(0,0);
 9         for(int i=1;i<=na+1;i++){
10             if(a[i].s==a[i-1].s) continue;
11             ra[a[i-1].s]=i-1; la[a[i].s]=i;
12         }
13         for(int i=1;i<=nb+1;i++){
14             if(b[i].s==b[i-1].s) continue;
15             rb[b[i-1].s]=i-1; lb[b[i].s]=i;
16         }
17         for(int i=1;i<=n;i++){
18             int lid=i-w[i],rid=i+w[i];
19             if(lid>0&&la[lid]){
20                 int id=lower_bound(a+la[lid],a+ra[lid]+1,st(lid,i))-a;
21                 if(id==ra[lid]+1) goto loop;
22                 ans[i]+=ra[lid]-id+1;
23             }
24             loop:;
25             if(rid<=n&&lb[rid]){
26                 int id=upper_bound(b+lb[rid],b+rb[rid]+1,st(rid,i))-b;
27                 ans[i]+=id-lb[rid];
28             }
29         }
30         for(int i=1;i<=n;i++) printf("%d ",ans[i]); 
31         return 0;
32     }    
15分的一条链代码

20分(9,10,11,12号点):考虑Si=1,不妨设整棵树的根为1,随后求出所有点的深度dep[i]。不难发现观察员i能观察到的选手必从其父亲方向跑来。开一个优先队列,以dep[Ti]为关键字,存储所有的Ti,借此维护f数组,f[i]表示从根节点跑来的人中经过点i(包括以i为终点的人)的人的数量。若点i满足w[i]==dep[i],则ans[i]=f[i],否则ans[i]=0。

 1 void dfs(int x,int fa){
 2     f[x]=fa;
 3     dep[x]=dep[fa]+1; dfn[x]=++t;
 4     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa)
 5     dfs(e[i].u,x);
 6     low[x]=t;
 7 }
 8 
 9 struct node{
10     int x; node(){x=0;}
11     node(int xx){x=xx;}
12     friend bool operator <(node a,node b){return dep[a.x]<dep[b.x];}
13 };
14 priority_queue<node> qx;
15 
16 int main(){
17         if(op==5){
18         dep[0]=-1; dfs(1,0);
19         for(int i=1;i<=m;i++){
20             if(!in[a[i].t]) 
21             qx.push(a[i].t);
22             have[a[i].t]++; 
23             in[a[i].t]=1;
24         }
25         while(!qx.empty()){
26             node u=qx.top(); qx.pop();
27             int x=u.x;
28             if(!in[f[x]]) qx.push(f[x]),in[f[x]]=1;
29             have[f[x]]+=have[x];
30         }
31         for(int i=1;i<=n;i++) 
32         if(w[i]==dep[i]) 
33         ans[i]=have[i];
34         for(int i=1;i<=n;i++) printf("%d ",ans[i]);
35         return 0;
36     }   
37 }

 

以上是关于NOIP2016 Day1 T2天天爱跑步的主要内容,如果未能解决你的问题,请参考以下文章

NOIP 2016 Day1 T2 天天爱跑步

Noip2016day1 天天爱跑步running

$NOIP 2016 Day1$ 模拟考试 题解报告

计蒜客NOIP模拟赛4 D2T2 跑步爱天天

NOIP2016天天爱跑步

Noip 2016 天天爱跑步 题解