2017.8.14 [NOIP2016]天天爱跑步 带思考的LCA.....
【题目描述】
小C同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。
这个游戏的地图可以看作一棵包含n个结点和n-1条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到n的连续正整数。
现在有m个玩家,第i个玩家的起点为Si,终点为Ti。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。(由于地图是一棵树,所以每个人的路径是唯一的)
小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点J 。 小C想知道每个观察员会观察到多少人?
注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点J作为终点的玩家: 若他在第Wj秒重到达终点,则在结点J的观察员不能观察到该玩家;若他正好在第Wj秒到达终点,则在结点的观察员可以观察到这个玩家。
【输入格式】
第一行有两个整数n和m。其中n代表树的结点数量,同时也是观察员的数量,m代表玩家的数量。
接下来n-1行每行两个整数u和v,表示结点u到结点v有一条边。
接下来一行n个整数,其中第j个整数为Wj,表示结点j出现观察员的时间。
接下来m行,每行两个整数Si和Ti,表示一个玩家的起点和终点。
对于所有的数据,保证1≤Si,Ti≤n,0≤ Wj ≤n。
【输出格式】
输出1行n个整数,第j个整数表示结点j的观察员可以观察到多少人。
【样例1输入】
6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
【样例1输出】
2 0 0 1 1 1
【样例2输入】
5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5
【样例2输出】
1 2 1 0 1
【提示】
对于1号点,W1=0,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共2人被观察到。
对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。
对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。
对于4号点,玩家1被观察到,共1人被观察到。
对于5号点,玩家2被观察到,共1人被观察到。
对于6号点,玩家3被观察到,共1人被观察到。
如果你的程序需要用到较大的栈空间(这通常意味着需要较深层数的递归),请务必仔细阅读选手目录下的文档running/stackpdf,以了解在最终评测时栈空间的限制与在当前工作环境下调整栈空间限制的方法。
solution
(我个人认为第5个点最强......)
每个人从S→T的路径有两种情况:
1.S和T都不是LCA
设S→T路径上一点是i
(1) 当i在S→LCA上时
只有满足 dep[S]==w[i]+dep[i]
(2) 当i在LCA→T上时
只有满足 len(S,T)-dep[T]==w[i]-dep[i]
第i个点的观察员才能看见这个人
2.S和T有一个是LCA或S==T
与上述情况类似
考虑两种情况之后问题就转化成 求满足上述关系式的点的个数
我们可以把S→T分成 S→LCA 和 LCA→T 两个路径
又发现 w[i]+dep[i] 和 w[i]-dep[i] 是两个定值
利用差分思想 在S标记+1 LCA-1 T+1 LCA-1 由于有些点信息不足,可以用vector
最后dfs统计ans,开两个数组a[i],b[i] (一个维护当前节点下面dep[S]的个数 一个维护len(S,T)-dep[T]的个数)
还有一种特殊情况 就是在S→T的路径中,LCA的观察员也会看见他,记得统计的时候要-1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<vector> 5 #define mem(a,b) memset(a,b,sizeof(a)) 6 using namespace std; 7 const int N=300066; 8 struct son 9 { 10 int v,next; 11 }; 12 son a1[N*3]; 13 int first[N*3],e; 14 inline void addbian(int u,int v) 15 { 16 a1[e].v=v; 17 a1[e].next=first[u]; 18 first[u]=e++; 19 } 20 21 int n,m; 22 int u,o,p; 23 int w[N],dep[N],fa[N]; 24 int q[N][43]; 25 26 inline void dfs1(int x) 27 { 28 for(int i=first[x];i!=-1;i=a1[i].next) 29 { 30 int temp=a1[i].v; 31 if(temp==fa[x])continue; 32 fa[temp]=x; 33 dep[temp]=dep[x]+1; 34 dfs1(temp); 35 } 36 } 37 38 inline void chu() 39 { 40 mem(q,-1); 41 for(int i=1;i<=n;++i) 42 q[i][0]=fa[i]; 43 for(int j=1;(1<<j)<=n;++j) 44 for(int i=1;i<=n;++i) 45 if(q[i][j-1]!=-1) 46 q[i][j]=q[q[i][j-1]][j-1]; 47 } 48 49 inline int LCA(int x,int y) 50 { 51 if(dep[x]<dep[y]) 52 swap(x,y); 53 int num=0; 54 while((1<<num)<=n)++num; 55 for(int j=num;j>=0;--j) 56 if(dep[x]-(1<<j)>=dep[y]) 57 x=q[x][j]; 58 if(x==y) 59 return x; 60 for(int j=num;j>=0;--j) 61 if(q[x][j]!=-1&&q[x][j]!=q[y][j]) 62 { 63 x=q[x][j]; 64 y=q[y][j]; 65 } 66 return fa[x]; 67 } 68 69 int sjia[N]; 70 vector<int> sjian[N],tjia[N],tjian[N]; 71 int a[N],b[N*2+100000]; 72 int ans[N]; 73 74 inline void dfs(int x) 75 { 76 int spre=a[w[x]+dep[x]]; 77 int tpre=b[w[x]-dep[x]+N]; 78 79 for(int i=first[x];i!=-1;i=a1[i].next) 80 { 81 int temp=a1[i].v; 82 if(temp==fa[x])continue; 83 dfs(temp); 84 } 85 86 a[dep[x]]+=sjia[x]; 87 88 ans[x]+=a[w[x]+dep[x]]-spre; 89 90 int qqq=sjian[x].size(); 91 for(int i=0;i<qqq;++i) 92 { 93 --a[sjian[x][i]]; 94 if(sjian[x][i]-dep[x]==w[x])//在这里减,只减去会受影响的lca,但是不会影响S→lca的点 95 --ans[x]; 96 } 97 //if(x==62) 98 // printf("ans[x]=%d\n",ans[x]); 99 100 qqq=tjia[x].size(); 101 for(int i=0;i<qqq;++i) 102 ++b[tjia[x][i]+N]; 103 104 ans[x]+=b[w[x]-dep[x]+N]-tpre; 105 106 qqq=tjian[x].size(); 107 for(int i=0;i<qqq;++i) 108 --b[tjian[x][i]+N]; 109 } 110 111 int main(){ 112 113 //freopen("runninga.in","r",stdin); 114 //freopen("runninga.out","w",stdout); 115 116 mem(first,-1); 117 118 scanf("%d%d",&n,&m); 119 for(int i=1;i<n;++i) 120 { 121 scanf("%d%d",&u,&o); 122 addbian(u,o); 123 addbian(o,u); 124 } 125 for(int i=1;i<=n;++i) 126 scanf("%d",&w[i]); 127 128 dfs1(1); 129 chu(); 130 131 for(int i=1;i<=m;++i) 132 { 133 scanf("%d%d",&u,&o); 134 int len,lca; 135 lca=LCA(u,o); 136 len=dep[u]-dep[lca]+dep[o]-dep[lca]; 137 138 //S→T 这条路径上 lca 做出贡献 139 ++sjia[u]; 140 sjian[lca].push_back(dep[u]); 141 //如果在这不加,对于其它有贡献的不是LCA的点也就没了 142 143 tjia[o].push_back(len-dep[o]); 144 tjian[lca].push_back(len-dep[o]); 145 } 146 147 dfs(1); 148 149 for(int i=1;i<=n;++i) 150 printf("%d ",ans[i]); 151 152 //while(1); 153 return 0; 154 }