ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

"蔚来杯"2022牛客暑期多校训练营3

2022-08-13 01:30:39  阅读:149  来源: 互联网

标签:ch int 蔚来 多校 long son 牛客 include define


A.Ancestor

给定两棵有\(n\)个节点的树\(A、B\),树上节点均有一个权值,给出\(k\)个关键点的编号\(x_1,x_2,...,x_k\),问有多少种方案,使得恰好去掉一个关键点后,剩余关键点在\(A\)中LCA的权值大于\(B\)中LCA的权值?


题解做法是预处理,预处理两个关键点序列在\(A,B\)两棵树上的前后缀LCA,然后枚举去掉的关键点,用前缀后缀LCA计算出剩余节点的LCA比较权值即可

但考场上我比较头铁,写了个虚树的做法(捂脸

对\(k\)个关键点求出虚树,当我们要去掉一个节点的时候,对该节点的情况进行分类讨论,并求出新的LCA即可

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 1e5;
int Point[N + 10], n, K;
struct VirtualTree {
	int pre[(N << 1) + 10], now[N + 10], child[(N << 1) + 10], tot;
	int Fa[N + 10], Num[N + 10], Root;
	void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
	void connect(int x, int y) { link(x, y), link(y, x); }
	void Dfs(int x, int fa) {
		Fa[x] = fa;
		for (int p = now[x]; p; p = pre[p]) {
			int son = child[p];
			if (son == fa)	continue;
			Dfs(son, x), Num[x]++;
		}
	}
	int solve(int x) {
		if (x == Root) {
			if (Num[x] > 1)	return Root;
			return child[now[Root]];
		}
		if (Fa[x] != Root || Num[x] || Num[Root] > 2)	return Root;
		for (int p = now[Root]; p; p = pre[p]) {
			int son = child[p];
			if (son != x)	return son;
		}
		return 0;
	}
}VTA, VTB;
struct Tree {
	int pre[(N << 1) + 10], now[N + 10], child[(N << 1) + 10], tot, Time;
	int W[N + 10], Deep[N + 10], Top[N + 10], Heavy[N + 10], Sz[N + 10], Fa[N + 10], Dfn[N + 10];
	void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
	void connect(int x, int y) { link(x, y), link(y, x); }
	void Dfs1(int x, int fa) {
		Deep[x] = Deep[Fa[x] = fa] + (Sz[x] = 1);
		for (int p = now[x]; p; p = pre[p]) {
			int son = child[p];
			if (son == fa)	continue;
			Dfs1(son, x), Sz[x] += Sz[son];
			if (!Heavy[x] || Sz[son] > Sz[Heavy[x]])
				Heavy[x] = son;
		}
	}
	void Dfs2(int x, int fa) {
		if (!x)	return;
		Dfn[x] = ++Time;
		Top[x] = Heavy[fa] == x ? Top[fa] : x;
		Dfs2(Heavy[x], x);
		for (int p = now[x]; p; p = pre[p]) {
			int son = child[p];
			if (son == fa || son == Heavy[x])	continue;
			Dfs2(son, x);
		}
	}
	int LCA(int x, int y) {
		while (Top[x] != Top[y]) {
			if (Deep[Top[x]] < Deep[Top[y]])	swap(x, y);
			x = Fa[Top[x]];
		}
		return Deep[x] < Deep[y] ? x : y;
	}
	void rebuild(VirtualTree& VT) {
		sort(Point + 1, Point + 1 + K, [&](int x, int y) {return Dfn[x] < Dfn[y]; });
		VT.Root = Point[1];
		for (int i = 2; i <= K; i++)	VT.Root = LCA(VT.Root, Point[i]);
		vector<int>stack;
		stack.push_back(VT.Root);
		for (int i = 1; i <= K; i++) {
			int x = Point[i], lca = LCA(x, stack.back());
			if (x == VT.Root)	continue;
			if (lca == stack.back()) {
				stack.push_back(x);
				continue;
			}
			while (stack.size() > 1) {
				int y = *prev(prev(stack.end()));
				if (Dfn[y] >= Dfn[lca])	VT.connect(y, stack.back()), stack.pop_back();
				else {
					if (lca != stack.back()) {
						VT.connect(lca, stack.back());
						stack.pop_back(), stack.push_back(lca);
					}
					break;
				}
			}
			stack.push_back(x);
		}
		while (stack.size() > 1) {
			VT.connect(stack.back(), *prev(prev(stack.end())));
			stack.pop_back();
		}
	}
	void Read(VirtualTree& VT) {
		for (int i = 1; i <= n; i++)	W[i] = read(0);
		for (int i = 2; i <= n; i++)	connect(read(0), i);
		Dfs1(1, 0), Dfs2(1, 0), rebuild(VT), VT.Dfs(VT.Root, 0);
	}
}A, B;
void solve() {
	int Cnt = 0;
	for (int i = 1; i <= K; i++) {
		int _A = VTA.solve(Point[i]);
		int _B = VTB.solve(Point[i]);
		Cnt += A.W[_A] > B.W[_B];
	}
	printf("%d\n", Cnt);
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	n = read(0), K = read(0);
	for (int i = 1; i <= K; i++)	Point[i] = read(0);
	A.Read(VTA), B.Read(VTB);
	solve();
	return 0;
}

B.Boss

将\(n\)个人派遣到\(K\)个城市,每个城市需要\(e_i\)个人,将第\(i\)个人派遣至第\(j\)个城市的代价为\(c_{i,j}\),问最小代价?\((1\leqslant n\leqslant 10^5,1\leqslant K\leqslant 10)\)


一个很显然的做法是费用流,但是点数边数太多,费用流没法跑

考虑费用流的过程,每次都是把一部分人从城市\(a\)挪到城市\(b\)。如果第\(i\)个人从城市\(a\)挪到了城市\(b\),那么其代价为\(-c_{a,i}+c_{b,i}\)。考虑到增广路只会经过每个点一次,所以如果有多个人可以从城市\(a\)到城市\(b\),必然会优先考虑代价小的人

故我们考虑建\(K\)个点代表城市,\(a\)到\(b\)的边就是所有在城市\(a\)中的人到城市\(b\)的代价。显然,这样的边数依然是\(O(nK)\)级别,但两点之间会有大量的重边,而上面提到,对于重边我们仅需要考虑其最短边即可。

故我们可以开\(K^2\)个堆去维护点与点之间的重边,这样就可以跑SPFA了。
初始将所有人放在城市1,并且每跑一次就要更新一次边集

SPFA总共跑\(n\)次,每次至多移动\(K\)个人所在的城市,故时间复杂为\(O(nk^3+nk\log n)\)

(代码咕咕咕)

C.Concatenation

给定\(n\)个字符串,求将它们拼接起来的字典序最小


直接排序,注意cmp判断函数里面带引用,以及先判断首字母再进行STL自带比较

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 2e6;
string s[N + 10];
bool cmp(const string& a,const string& b) { return a[0] != b[0] ? a[0] < b[0] : a + b < b + a; }
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0);
	for (int i = 1; i <= n; i++)	cin >> s[i];
	sort(s + 1, s + 1 + n, cmp);
	for (int i = 1; i <= n; i++)
		printf("%s", s[i].c_str());
	return 0;
}

D.Directed

给定一棵\(n\)个节点的树和一个起点\(s\),1号节点为终点,随机选择\(k\)条边变成单向边,问从起点开始,在树上随机游走到达终点的期望步数?

首先不考虑反向边,我们设\(F_u\)表示节点\(u\)随机走到其父亲的期望步数,可以得到式子:\(F_u=\frac{1}{d_u}+\sum\limits_{u\rightarrow v}\frac{1+F_v+F_u}{d_u}\),其中\(d_u\)表示点\(u\)的度数

移项可得\(F_u=d_u+\sum\limits_{u\rightarrow v}F_v\),考虑递归套入这个式子,可以发现除了其到父亲的边之外,每条边都被计算了两次,不难得出\(F_u=2\times sz_u-1\)

考虑单向边的影响,可以发现一条单向边\(<u,Fa_u>\)是不会对其子树造成任何影响的,但是它会使其所有祖先的\(F'\)减少\(2\times sz_u\)。换而言之,一个点\(y\)对祖先\(x\)有贡献当且仅当它们之间不存在单向边,如果\(x,y\)之间的距离为\(l\),则\(y\)对\(F_x\)的贡献为\(\binom{n-i-1}{k}/\binom{n-1}{k}\)

此外,注意到我们要统计的是\(s\)到1路径上所有点的\(F\)值之和,可以发现对于树上任意一个点而言,其对祖先的贡献在答案所求的路径上均为连续的一整段,故我们可以对这一整段连续的贡献进行与预处理,然后区间求和即可。

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 1e6, P = 998244353;
int pre[(N << 1) + 10], now[N + 10], child[(N << 1) + 10], tot;
int Deep[N + 10], F[N + 10];
void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
void connect(int x, int y) { link(x, y), link(y, x); }
void Dfs(int x, int fa) {
	Deep[x] = Deep[F[x] = fa] + 1;
	for (int p = now[x]; p; p = pre[p]) {
		int son = child[p];
		if (son == fa)	continue;
		Dfs(son, x);
	}
}
bool Vis[N + 10];
int Inv[N + 10], S[N + 10], Ans, L, R;
int solve(int x, int y) { return y ? (S[x + y - 1] - S[y - 1] + P) % P : S[x + y - 1]; }
void Dfs(int x) {
	if (Deep[x])
		Ans = (Ans + solve(L, R)) % P;
	for (int p = now[x]; p; p = pre[p]) {
		int son = child[p];
		if (son == F[x])	continue;
		L += Vis[son], R += Vis[son] ^ 1;
		Dfs(son);
		L -= Vis[son], R -= Vis[son] ^ 1;
	}
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0), k = read(0), s = read(0);
	for (int i = 1; i < n; i++) {
		int x = read(0), y = read(0);
		connect(x, y);
	}
	Deep[0] = -1;
	Dfs(1, 0);
	for (int x = s; x; x = F[x])	Vis[x] = 1;
	Inv[0] = Inv[1] = S[0] = 1;
	for (int i = 2; i <= n; i++)	Inv[i] = 1ll * (P - P / i) * Inv[P % i] % P;
	for (int i = 1; i <= n; i++)	S[i] = 1ll * S[i - 1] * (n - i - k) % P * Inv[n - i] % P;
	for (int i = 1; i <= n; i++)	S[i] = (S[i] + S[i - 1]) % P;
	Dfs(1);
	Ans = (2ll * Ans - Deep[s] + P) % P;
	printf("%d\n", Ans);
	return 0;
}

E.Electrician

给定一个圆,圆上有\(n\)个等距点,和\(m\)条等长的弦。现在要在圆上铺设两条电线,一条从1到\(n\),另一条从2到\(n-1\),电线可以沿着圆环铺设,也可以沿着弦铺设,在弦与弦相交的地方也可以转到另一根弦上铺设。问有多少种方案使得电线铺设不相交?


容斥+dp,咕咕咕

F.Fief

给定一个无向图,每次询问两点\(x,y\),问是否存在一个\(n\)的排列,使得第一个元素为\(x\),最后一个元素是\(y\),且排列的任意一个前缀、后缀都在图中连通


考虑化简问题,对于树上问题而言,有解当且仅当图为一条链,且询问两点为链的端点

对于一般图而言,有解当且仅当建出圆方树后方点构成一条链,询问两点位于两个方点端点,且两个点不能是割点

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 1e5, M = 2e5;
int pre[(M << 1) + 10], now[N + 10], child[(M << 1) + 10], tot;
int Dfn[N + 10], Low[N + 10], stack[N + 10], Cnt[N + 10], Time, top, Number, Root;
void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
void connect(int x, int y) { link(x, y), link(y, x); }
vector<int>bcc[N + 10];
bool Vis[N + 10];
void Tarjan(int x, int fa) {
	Dfn[x] = Low[x] = ++Time;
	stack[++top] = x;
	if (x == Root && !now[x]) {
		bcc[++Number].push_back(x);
		return;
	}
	int cnt = 0;
	for (int p = now[x]; p; p = pre[p]) {
		int son = child[p];
		if (!Dfn[son]) {
			Tarjan(son, x);
			Low[x] = min(Low[x], Low[son]);
			if (Dfn[x] <= Low[son]) {
				Number++, cnt++;
				if (x != Root || cnt > 1)	Vis[x] = 1;
				bcc[Number].push_back(x);
				while (stack[top] != son)
					bcc[Number].push_back(stack[top--]);
				bcc[Number].push_back(stack[top--]);
			}
		}
		else Low[x] = min(Low[x], Dfn[son]);
	}
}
struct Edge {
	int x, y;
	Edge(int _x = 0, int _y = 0) :x(_x), y(_y) {}
};
int Deg[M + 10];
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0), m = read(0);
	vector<Edge>edges;
	for (int i = 1; i <= m; i++) {
		int x = read(0), y = read(0);
		connect(x, y);
	}
	int res = 0;
	for (int i = 1; i <= n; i++) {
		if (!Dfn[i]) {
			Root = i;
			Tarjan(i, 0), res++;
		}
	}
	int _N = Number;
	for (int i = 1; i <= n; i++)
		if (Vis[i]) 
			Cnt[i] = ++_N;
	for (int i = 1; i <= Number; i++) {
		for (auto p : bcc[i]) {
			if (Vis[p])
				Deg[i]++, Deg[Cnt[p]]++;
			else
				Cnt[p] = i;
		}
	}
	bool Flag = (res == 1);
	for (int i = 1; i <= _N; i++)
		Flag &= Deg[i] <= 2;
	int q = read(0);
	for (int i = 1; i <= q; i++) {
		int x = Cnt[read(0)], y = Cnt[read(0)];
		if (!Flag) {
			printf("NO\n");
			continue;
		}
		if (!Deg[x] || !Deg[y]) {
			printf("YES\n");
			continue;
		}
		if (Deg[x] == 1 && Deg[y] == 1 && x != y) {
			printf("YES\n");
			continue;
		}
		printf("NO\n");
	}
	return 0;
}

G.Geometry

给定二维平面中两个凸包,问他们相撞的时刻


计算几何难难,咕咕咕

H.Hacker

给定一个长为\(n\)的主串\(s\),再给定\(m\)个数\(V_i\),然后给定\(k\)个长为\(m\)的匹配串\(t_i\),对于每个\(t_i\),若其子串\(t_i[l...r]\)为\(s\)的子串,则记\(C_{l,r}=\sum\limits_{j=l}^rV_j\),求每个串\(t_i\)中\(C_{l,r}\)的最大值


考虑对主串建SAM,对每个模式串在SAM上进行匹配,对于\(t_i\)的位置\(j\)而言,记其匹配节点在SAM上为\(p\),则其所能匹配的最大长度为\(F_p\rightarrow len\),\(F_p\)为其在SAM上的父节点

求出最长匹配长度后,再用线段树求一次区间最大连续子段和即可

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 1e5;
int V[N + 10];
struct ST {
#define ls (p<<1)
#define rs (p<<1|1)
	struct node {
		ll l, r, now, sum;
		node(ll _l = 0, ll _r = 0, ll _n = 0, ll _s = 0) { l = _l, r = _r, now = _n, sum = _s; }
		void init(ll v) { l = r = now = max(0ll, sum = v); }
		friend node operator+(const node& A, const node& B);
	};
	friend node operator +(const node& A, const node& B) {
		node C;
		C.l = max(A.l, A.sum + B.l);
		C.r = max(A.r + B.sum, B.r);
		C.now = max(max(A.now, B.now), A.r + B.l);
		C.sum = A.sum + B.sum;
		return C;
	}
	node Tree[(N << 2) + 10];
	void update(int p) { Tree[p] = Tree[ls] + Tree[rs]; }
	void build(int p, int l, int r) {
		if (l == r) {
			Tree[p].init(V[l]);
			return;
		}
		int mid = (l + r) >> 1;
		build(ls, l, mid);
		build(rs, mid + 1, r);
		update(p);
	}
	node Query(int p, int l, int r, int L, int R) {
		if (L <= l && r <= R)	return Tree[p];
		int mid = (l + r) >> 1;
		node res = 0;
		if (L <= mid)	res = res + Query(ls, l, mid, L, R);
		if (R > mid)	res = res + Query(rs, mid + 1, r, L, R);
		return res;
	}
#undef ls
#undef rs
}st;//segment tree
struct SAM {
	struct node {
		int maxl, fa, son[26];
		node() {
			maxl = fa = 0;
			memset(son, 0, sizeof(son));
		}
		node operator&=(const node& ots) {
			maxl = ots.maxl, fa = ots.fa;
			memcpy(son, ots.son, sizeof(ots.son));
			return *this;
		}
	}step[(N << 1) + 10];
	int root, last, tot;
	SAM() { root = last = tot = 1; }
	void insert(int ch) {
		int p = last, np = ++tot;
		step[np].maxl = step[p].maxl + 1;
		while (p && !step[p].son[ch]) {
			step[p].son[ch] = np;
			p = step[p].fa;
		}
		if (!p)	step[np].fa = root;
		else {
			int q = step[p].son[ch];
			if (step[q].maxl != step[p].maxl + 1) {
				int nq = ++tot;
				step[nq] = step[q];
				step[nq].maxl = step[p].maxl + 1;
				step[np].fa = step[q].fa = nq;
				while (p && step[p].son[ch] == q) {
					step[p].son[ch] = nq;
					p = step[p].fa;
				}
			}
			else	step[np].fa = q;
		}
		last = np;
	}
	ll solve(char* s, int m) {
		int len = strlen(s + 1), p = root, L = 0;
		ll Ans = 0;
		for (int i = 1; i <= len; i++) {
			while (!step[p].son[s[i] - 'a'] && p != root)
				p = step[p].fa, L = step[p].maxl;
			if (step[p].son[s[i] - 'a'])
				p = step[p].son[s[i] - 'a'], L++;
			Ans = max(Ans, st.Query(1, 1, m, i - L + 1, i).now);
		}
		return Ans;
	}
}sam;
char s[N + 10];
int main() {
	int n = read(0), m = read(0), k = read(0);
	UNUSED(k);
	scanf("%s", s + 1);
	for (int i = 1; i <= n; i++)	sam.insert(s[i] - 'a');
	for (int i = 1; i <= m; i++)
		V[i] = read(0);
	st.build(1, 1, m);
	for (int i = 1; i <= k; i++) {
		scanf("%s", s + 1);
		printf("%lld\n", sam.solve(s, m));
	}
	return 0;
}

I.Ice Drinking

记\(x\)为一个随机\(n\)阶排列中\(a_i=i\)的个数,求\(x^k\)的期望值


数论难难,咕咕咕

J.Journey

一个城市中有若干个十字路口,右转不需要等红灯,直行左转掉头都需要等红灯,求起点到终点最少需要等多少次红灯?


其实是个裸的最短路,但本题建图的方式稍有区别

本题为路口编号,道路则由\(<a,b>\)的形式给出。故我们将道路看做点,点与点之间的权值为是否需要等红灯,只要将图建好即可跑最短路了

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const ll N = 5e9;
template<typename T>
class MyArray {
private:
	T* S;
	int Msize;
	void expandSize();
public:
	MyArray(int size = 1) { S = new T[Msize = size]; }
	~MyArray() { delete[] S; }
	MyArray(const MyArray& tis) = delete;
	MyArray& operator=(const MyArray& tis);
	bool operator !()const { return !Msize; }
	T& operator [](int index);
};
template<typename T>
void MyArray<T>::expandSize() {
	T* temp = S;
	S = new T[Msize << 1];
	for (int i = 0; i < Msize; i++)
		S[i] = temp[i];
	Msize <<= 1;
	delete[] temp;
}
template<typename T>
MyArray<T>& MyArray<T>::operator=(const MyArray& tis) {
	S = new T[Msize = tis.Msize];
	for (int i = 0; i < tis.Msize; i++)
		S[i] = tis.S[i];
	return *this;
}
template<typename T>
T& MyArray<T>::operator [](int index) {
	while (index >= Msize)
		expandSize();
	return S[index];
}

MyArray<int>pre, now, child, val;
int tot;
void link(int x, int y, int v) { pre[++tot] = now[x], now[x] = tot, child[tot] = y, val[tot] = v; }
unordered_map<int, int>Dis;
unordered_map<ll, int>Point;
unordered_map<int, bool > Vis;
struct node {
	int x, v;
	node(int _x = 0, int _v = 0) { x = _x, v = _v; }
	bool operator <(const node& ots)const { return v > ots.v; }
};
void Dijkstra(int S, int T) {
	priority_queue<node>Heap;
	Heap.push(node(S, Dis[S] = 0));
	while (!Heap.empty()) {
		node Now = Heap.top(); Heap.pop();
		if (Vis[Now.x])	continue;
		Vis[Now.x] = 1;
		for (int p = now[Now.x]; p; p = pre[p]) {
			int son = child[p];
			if (Dis[son] > Dis[Now.x] + val[p]) {
				Dis[son] = Dis[Now.x] + val[p];
				Heap.push(node(son, Dis[son]));
			}
		}
	}
	printf("%d\n", Dis[T] >= iMax ? -1 : Dis[T]);
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0), cnt = 0;
	for (int i = 1; i <= n; i++) {
		vector<int>c(4, 0);
		for (int k = 0; k < 4; k++) {
			c[k] = read(0);
			if (c[k]) {
				if (!Point[N * i + c[k]]) {
					Point[N * i + c[k]] = ++cnt;
					now[cnt] = 0;
				}
				if (!Point[N * c[k] + i]) {
					Point[N * c[k] + i] = ++cnt;
					now[cnt] = 0;
				}
			}
		}
		for (int a = 0; a < 4; a++) {
			for (int b = 0; b < 4; b++) {
				if (!c[a] || !c[b])	continue;
				if ((a + 1) % 4 != b)
					link(Point[N * c[a] + i], Point[N * i + c[b]], 1);
				else
					link(Point[N * c[a] + i], Point[N * i + c[b]], 0);
			}
		}
	}
	int s1 = read(0), s2 = read(0), t1 = read(0), t2 = read(0);
	int S = Point[N * s1 + s2], T = Point[N * t1 + t2];
	for (int i = 1; i <= cnt; i++)	Dis[i] = iMax;
	Dijkstra(S, T);
	return 0;
}

标签:ch,int,蔚来,多校,long,son,牛客,include,define
来源: https://www.cnblogs.com/Wolfycz/p/16581836.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有