bzoj3700

Description

 众所周知,Hzwer学长是一名高富帅,他打算投入巨资发展一些小城市。
 Hzwer打算在城市中开N个宾馆,由于Hzwer非常壕,所以宾馆必须建在空中,但是这样就必须建立宾馆之间的连接通道。机智的Hzwer在宾馆中修建了N-1条隧道,也就是说,宾馆和隧道形成了一个树形结构。
 Hzwer有时候会花一天时间去视察某个城市,当来到一个城市之后,Hzwer会分析这些宾馆的顾客情况。对于每个顾客,Hzwer用三个数值描述他:(S, T, V)表示该顾客这天想要从宾馆S走到宾馆T,他的速度是V。
 Hzwer需要做一些收集一些数据,这样他就可以规划他接下来的投资。
 其中有一项数据就是收集所有顾客可能的碰面次数。
 每天清晨,顾客同时从S出发以V的速度前往T(注意S可能等于T),当到达了宾馆T的时候,顾客显然要找个房间住下,那么别的顾客再经过这里就不会碰面了。特别的,两个顾客同时到达一个宾馆是可以碰面的。同样,两个顾客同时从某宾馆出发也会碰面。

Input

 第一行一个正整数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

Output

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

Sample Input

1
3
1 2 1
2 3 1
3
1 3 2
3 1 1
1 2 3
1
0

Sample Output

2
0

HINT

【数据规模】

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

感觉就是这题很丧心病狂吧。路径求交,还要判断是否相遇,所以就抄了一份黄学长代码QAQ

     路径求交,假设a,b,c,d,那么c到路径ab的最近点必然是它们交点的其中一个端点。
假如c到ab的最近点为f1,d到ab的最近点为f2,a到cd的最近点为f3,b到cd的最近点为f4,若是(f1,f2)=(f3,f4)则路径有交,交路径的两端点就是f1和f2.
现在讨论c到ab的最近点。

这个最近的点怎么求呢?

我们以c为例:

此时令r=LCA(a,b),假设c不在r的子树中,即LCA(c,r)!=r那么r就是我们要找的这个点

如果LCA(c,r)==r有三种情况:

第一种是LCA(a,c)==LCA(b,c)==r此时依然是r

另一种是LCA(a,c)!=r 那么此时就是LCA(a,c)

还有一种 LCA(b,c)!=r 此时是LCA(b,c)

找到路径交集后就瞎jb乱搞吧,判断交路径只有一个点的情况,比如说顾客方向相反啦,相同的时候怎么样怎么样啦。

代码是抄的,然后因为以前发现树剖求lca不比欧拉序求lca慢所以就很自信地写了树剖。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define double long double
using namespace std;
const int MAXN = 100005;
int first[MAXN], next[MAXN << 1], go[MAXN << 1], way[MAXN << 1], t;
int pos[MAXN], top[MAXN], size[MAXN], son[MAXN], fa[MAXN], dep[MAXN], dis[MAXN], dfn[MAXN];
int n, m, i, j, k, l, x, y, z, begi[1005], end[1005], e[4], sd[1005], u, v, T;
bool vis[MAXN];
inline int get()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	int res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
	res = res * 10 + c - 48;
	return res;
}
inline void add(const int &x, const int &y, const int &z)
{
	next[++t] = first[x]; first[x] = t; go[t] = y; way[t] = z;
	next[++t] = first[y]; first[y] = t; go[t] = x; way[t] = z;
}
inline void dfs(int now, int fat)
{
	size[now] = 1; dfn[now] = ++t;
	int son1 = 0, son2 = 0;
	for(int i = first[now]; i; i = next[i])
		if (go[i] != fat)
		{
			dep[go[i]] = dep[now] + 1;
			dis[go[i]] = dis[now] + way[i];
			fa[go[i]] = now;
			dfs(go[i], now);
			if (size[go[i]] > son1) son1 = size[go[i]], son2 = go[i];
			size[now] += size[go[i]];
		}
	son[now] = son2;
}
inline void dfs1(int now)
{
	pos[now] = ++t;
	if (son[now])
	{
		top[son[now]] = top[now];
		dfs1(son[now]);
	}
	for(int i = first[now]; i; i = next[i])
		if (!pos[go[i]])
		{
			top[go[i]] = go[i];
			dfs1(go[i]);
		}
}
inline int getlca(int x, int y)
{
	while (top[x] != top[y])
	{
		if (dep[top[x]] < dep[top[y]]) swap(x, y);
		x = fa[top[x]];
	}
	if (pos[x] < pos[y]) return x;
	else return y;
}
inline int getdis(int x, int y)
{
	return dis[x] + dis[y] - (dis[getlca(x, y)] << 1);
}
inline int getclose(int a, int b, int c)
{
	int r = getlca(a, b);
	if (dfn[c] < dfn[r] || dfn[c] >= dfn[r] + size[r]) return r;
	int lca1 = getlca(a, c);
	if (lca1 != r) return lca1;
	int lca2 = getlca(b, c);
	if (lca2 != r) return lca2;
	return r;
}
inline void getpath(int a, int b, int c, int d, int &u, int &v)
{
	e[0] = getclose(a, b, c);
	e[1] = getclose(a, b, d);
	e[2] = getclose(c, d, a);
	if (e[2] != e[0] && e[2] != e[1]) {u = v = 0; return;}
	e[3] = getclose(c, d, b);
	if (e[3] != e[0] && e[3] != e[1]) {u = v = 0; return;}
	u = e[0]; v = e[1];
}
inline bool check(int x, int y)
{
	if (begi[x] == begi[y]) return 1;
	getpath(begi[x], end[x], begi[y], end[y], u, v);
	if (!u) return 0;
	if (u == v) return ((double)getdis(begi[x], u) / sd[x] == (double)getdis(begi[y], u) / sd[y]);
	if (getdis(u, begi[x]) > getdis(v, begi[x])) swap(u, v);
	if (getclose(u, v, begi[x]) == getclose(u, v, begi[y]))
	{
		double tim1 = (double)getdis(begi[x], u) / sd[x], tim2 = (double)getdis(begi[y], u) / sd[y];
		if (tim1 == tim2) return 1;
		if (tim1 > tim2) swap(x, y);
		return ((double)getdis(begi[x], v) / sd[x] >= (double)getdis(begi[y], v) / sd[y]);
	}
	return ((double)getdis(begi[x], u) / sd[x] <= (double)getdis(begi[y], u) / sd[y] && 
			(double)getdis(begi[y], v) / sd[y] <= (double)getdis(begi[x], v) / sd[x]);
}
int main()
{
	cin >> T;
	while (T --)
	{
		memset(first, 0, sizeof(first));
		memset(pos, 0, sizeof(pos));
		memset(vis, 0, sizeof(vis));
		dep[1] = dis[1] = t = 0;
		n = get();
		for(i = 1; i < n; i ++)
		{
			x = get(); y = get(); z = get();
			add(x, y, z);
		}
		t = 0;
		dfs(1, 0);
		t = 0;
		top[1] = 1;
		dfs1(1);
		m = get();
		for(i = 1; i <= m; i ++)
			begi[i] = get(), end[i] = get(), sd[i] = get();
		int ans = 0;
		for(i = 1; i < m; i ++)
			for(j = 1 + i; j <= m; j ++)
				if (check(i, j)) ans ++;
		printf("%d\n", ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值