发展城市 bzoj 3700

Posted 草根柴鸡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了发展城市 bzoj 3700相关的知识,希望对你有一定的参考价值。

发展城市

【问题描述】

众所周知,Hzwer学长是一名高富帅,他打算投入巨资发展一些小城市。

Hzwer打算在城市中开N个宾馆,由于Hzwer非常壕,所以宾馆必须建在空中,但是这样就必须建立宾馆之间的连接通道。机智的Hzwer在宾馆中修建了N-1条隧道,也就是说,宾馆和隧道形成了一个树形结构。

Hzwer有时候会花一天时间去视察某个城市,当来到一个城市之后,Hzwer会分析这些宾馆的顾客情况。对于每个顾客,Hzwer用三个数值描述他:(S, T, V)表示该顾客这天想要从宾馆S走到宾馆T,他的速度是V。

Hzwer需要做一些收集一些数据,这样他就可以规划他接下来的投资。

其中有一项数据就是收集所有顾客可能的碰面次数。

每天清晨,顾客同时从S出发以V的速度前往T(注意S可能等于T),当到达了宾馆T的时候,顾客显然要找个房间住下,那么别的顾客再经过这里就不会碰面了。特别的,两个顾客同时到达一个宾馆是可以碰面的。同样,两个顾客同时从某宾馆出发也会碰面。

【输入格式】

第一行一个正整数T(1<=T<=20),表示Hzwer发展了T个城市,并且在这T个城市分别视察一次。

对于每个T,第一行有一个正整数N(1<=N<=10^5)表示Hzwer在这个城市开了N个宾馆。

接下来N-1行,每行三个整数X,Y,Z表示宾馆X和宾馆Y之间有一条长度为Z的隧道

再接下来一行M表示这天顾客的数量。

紧跟着M行每行三个整数(S, T, V)表示该顾客会从宾馆S走到宾馆T,速度为v

【输出格式】

 对于每个T,输出一行,表示顾客的碰面次数。

【样例输入】

1 2 1 

2 3 1 

1 3 2 

3 1 1 

1 2 3 

【样例输出】

2

【数据规模】

1<=T<=20   1<=N<=10^5   0<=M<=10^3   1<=V<=10^6   1<=Z<=10^3


题解:

主要算法:最近公共祖先(Lca);

题意就是求两两树上路径是否能在相交路径上相遇

求相交路径自然就要求出最近公共祖先

╰( ̄▽ ̄)╭为了卡常数,使用O(1)求Lca的方法(当然,某位dalao用了O(log2(n))的树剖,并真的好像跑得确实稍微快那么一点~~~)

首先处理出树的欧拉序列(就是把每个点入栈和出栈记录下来,那么两个点的Lca就是它们组成的区间中深度最小的点)

用Rmq处理区间最值和对应的点就好啦

有了Lca,两个点的距离自然好求

那么我们就解决了问题的子问题的子问题的两个子问题啦

先考虑如何求出在一条路径上与某个点距离最近的点,记为Closest(a,b,c)

假设我们要求出(a,b)路径上的距离c最近的点

记x=Lca(a,b),y=Lca(c,a),z=Lca(c,b)

首先如果c不在x的子树内,由于树上最近路径唯一,x就是在(a,b)路径上与c最近的点

否则如果y不为x,这说明c和a同时属于x某个孩子的子树,那么y就是最近点,z同理

再否则说明a,b,c分属x三个孩子的子树,那么x还是最近点

至此我们解决了求路径上与点最近的点的问题

我们再来考虑如何求出两条路径的交这个子问题(对,没错,上面那个就是子问题的子问题,也是问题的子问题)

如果两条路径有交,那么第一条路径的两个点与第一条路径的两个最近点组成的路径必定与第二条路径的两个点与第一条路径的两个最近点组成的路径相等

有点点长(不是打错了(废话好多(自己吐槽是什么鬼))),冗长的自然语言啊

记两条路径为(a,b)和(c,d)

记abc为Close(a,b,c),其他同理

那么(abc,abd)=(cda,cdb)(这是无序的)

又一个问题解决了

最后考虑是否相遇

起点相同啊,没有相交路径啊,相交路径只有一个点啊,这些判一判就好啦

设两个人为x,y

设相交路径为u,v

设两条路径(两个人)的起点、终点为sx,tx,sy,ty

记Time(x,y)为x为起点,y为终点,以以x为起点的人的速度为速度所需的时间(再次重申没有打错)

求相遇当然要判断同向异向,判断的话考虑Closest(u,v,sx)是否等于Closest(u,v,sy),两个的含义分别是求两个点在相交路径上的起点,剩下的就显而易见了

为了方便,取u为x在(u,v)上的起点

异向的话只要满足一个在到达起点时另一个还未达到终点,那么它们必定相遇,判断就是Time(sx,u)<=Time(sy,u)且Time(sy,v)<=Time(sx,v)

同向的话只要满足先到达起点的后到达终点就行了,判断就看代码实现吧

求时间的话要用double啥的

由于我们只要求大小关系,交叉相乘即可

至此我们结束了整个问题(os:怎么这么长~~~~~~)

其实我是%了黄学长代码的<(* ̄▽ ̄*)/(那你还bb这么多)

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<cstdio>
  6 #include<cmath>
  7 using namespace std;
  8 const int logn = 19;
  9 const int maxn = 300233;
 10 int lea[maxn][logn], dot[maxn][logn];
 11 int num, pos[maxn], dfn[maxn], dep[maxn];
 12 long long dis[maxn];
 13 int tot, nex[maxn], fir[maxn], ver[maxn], val[maxn];
 14 int powe[logn], loga[maxn];
 15 int T;
 16 int n, m;
 17 int x, y, z;
 18 int ans;
 19 int s[maxn], t[maxn], vel[maxn];
 20 struct couple
 21 {
 22     int x, y;
 23 };
 24 inline void Scan(int &x)
 25 {
 26     char c;
 27     bool o = false;
 28     while((c = getchar()) < 0 || c > 9)
 29         if(c == -) o = true;
 30     x = c - 0;
 31     while((c = getchar()) >= 0 && c <= 9)
 32         x = x * 10 + c - 0;
 33     x = (o) ? -x : x;
 34 }
 35 inline void Clear()
 36 {
 37     tot = num = ans = 0;
 38     for(int i = 1; i <= n; ++i) fir[i] = 0;
 39 }
 40 inline void Ins(int x, int y, int z)
 41 {
 42     nex[++tot] = fir[x];
 43     fir[x] = tot;
 44     ver[tot] = y;
 45     val[tot] = z;
 46 }
 47 inline void Table()
 48 {
 49     powe[0] = 1;
 50     loga[1] = 0;
 51     for(int i = 1; i < logn; ++i) powe[i] = powe[i - 1] << 1, loga[powe[i]] = 1;
 52     for(int i = 1; i < maxn; ++i) loga[i] += loga[i - 1];
 53 }
 54 inline void Rmq()
 55 {
 56     for(int i = 1; i <= num; ++i)
 57     {
 58         dot[i][0] = dfn[i];
 59         lea[i][0] = dep[dfn[i]];
 60     }
 61     for(int j = 1; j <= loga[num] + 1; ++j)
 62         for(int i = 1; i <= num; ++i)
 63         {
 64             int k = i + powe[j - 1];
 65             if(k >= num) continue;
 66             if(lea[i][j - 1] < lea[k][j - 1])
 67             {
 68                 lea[i][j] = lea[i][j - 1];
 69                 dot[i][j] = dot[i][j - 1];
 70             }
 71             else
 72             {
 73                 lea[i][j] = lea[k][j - 1];
 74                 dot[i][j] = dot[k][j - 1];
 75             }
 76         }
 77 }
 78 inline int Lca(int x, int y)
 79 {
 80     x = pos[x], y = pos[y];
 81     if(x > y) swap(x, y);
 82     int len = loga[y - x + 1];
 83     int mid = y - powe[len] + 1;
 84     if(lea[x][len] < lea[mid][len]) return dot[x][len];
 85     return dot[mid][len];
 86 }
 87 inline long long Dis(int x, int y)
 88 {
 89     return dis[x] + dis[y] - (dis[Lca(x, y)] << 1);
 90 }
 91 void Dfs(int u, int f)
 92 {
 93     pos[u] = ++num;
 94     dfn[num] = u;
 95     for(int i = fir[u]; i; i = nex[i])
 96     {
 97         int v = ver[i];
 98         if(v == f) continue;
 99         dep[v] = dep[u] + 1;
100         dis[v] = dis[u] + val[i];
101         Dfs(v, u);
102         dfn[++num] = u;
103     }
104 }
105 inline int Close(int a, int b, int c)
106 {
107     int x = Lca(a, b);
108     if(x != Lca(x, c)) return x;
109     int y = Lca(a, c);
110     if(x != y) return y;
111     int z = Lca(b, c);
112     if(x != z) return z;
113     return x;
114 }
115 inline couple Path(int a, int b, int c, int d)
116 {
117     int abc = Close(a, b, c);
118     int abd = Close(a, b, d);
119     int cda = Close(c, d, a);
120     int cdb = Close(c, d, b);
121     if(abc > abd) swap(abc, abd);
122     if(cda > cdb) swap(cda, cdb);
123     if(abc == cda && abd == cdb) return (couple) {abc, abd};
124     return (couple) {-1, -1};
125 }
126 inline bool Meet(int x, int y)
127 {
128     if(s[x] == s[y]) return true;
129     couple z = Path(s[x], t[x], s[y], t[y]);
130     int u = z.x, v = z.y;
131     if(u < 0) return false;
132     if(u == v) return Dis(s[x], u) * vel[y] == Dis(s[y], u) * vel[x];
133     if(Close(u, v, s[x]) == v) swap(u, v);
134     if(Close(u, v, s[x]) != Close(u, v, s[y]))
135     {
136         bool fa = Dis(s[x], u) * vel[y] <= Dis(s[y], u) * vel[x];
137         bool fb = Dis(s[x], v) * vel[y] >= Dis(s[y], v) * vel[x];
138         return fa && fb;
139     }
140     long long a = Dis(s[x], u) * vel[y];
141     long long b = Dis(s[y], u) * vel[x];
142     if(a == b) return true;
143     if(a > b) swap(x, y);
144     return Dis(s[x], v) * vel[y] >= Dis(s[y], v) * vel[x];
145 }
146 int main()
147 {
148     memset(lea, 127, sizeof(lea));
149     Table();
150     Scan(T);
151     while(T--)
152     {
153         Clear();
154         Scan(n);
155         for(int i = 1; i < n; ++i)
156         {
157             Scan(x), Scan(y), Scan(z);
158             Ins(x, y, z);
159             Ins(y, x, z);
160         }
161         Dfs(1, 0);
162         Rmq();
163         Scan(m);
164         for(int i = 1; i <= m; ++i) Scan(s[i]), Scan(t[i]), Scan(vel[i]);
165         for(int i = 1; i <= m; ++i)
166             for(int j = i + 1; j <= m; ++j)
167                 ans += Meet(i, j);
168         printf("%d\n", ans);
169     }
170 }

 

 

以上是关于发展城市 bzoj 3700的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj3730] 震波

BZOJ 3730 震波

点分树BZOJ3730 震波

BZOJ3730震波 动态树分治+线段树

BZOJ3091 城市旅行 LCT

BZOJ2001 HNOI2010 城市建设