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

A.Falfa with Polygon

给定平面内\(n\)个点和\(k\),求在\(n\)个点中选出\(k\)个点,使得这\(k\)个点的凸包周长最长


决策单调性DP + 矩乘优化,咕咕咕

B.light

给定一个有厚度的凸多边形围墙和一个点光源,问围墙内有多少面积有光


计算几何模拟 + 凸包面积交,咕咕咕

A,B两人在玩Nim游戏,必胜的一方希望尽快结束游戏,必败的一方希望尽慢结束游戏。问最优策略下,“游戏结束时的操作数”和“第一次可能进行的操作数”?


首先,关于Nim游戏的必胜必败讨论详见该博客:浅谈算法——博弈论

其次,对于必败者而言,其可以保证每次双方都只能取一个石头。具体而言,考虑从lowbit(\(a_i\))最小(记为\(x\))的那一堆石头中取出一颗,则整体异或值变为\(2x-1\),则必胜方只能从另一个lowbit(\(a_j\))=\(x\)的石子堆中取出一颗才能保证异或值依然为零

故我们分两种情况讨论:

  • 先手必胜,则第一次取走\(x\)个石子使得石子异或和为0,操作数即为剩余石头总数+1
  • 先手必败,则第一次取走石子数为1,再考虑每一堆取走1个石子后,后手是否只能取一颗石子。由于取走后异或值只可能有\(\log n\)种情况,故复杂度是\(O(n\log n)\)
/*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 A[N + 10];
bool solve(int x, int n) {
	for (int i = 1; i <= n; i++) {
		int res = A[i] & x;
		if (res < (x + 1) >> 1)	continue;
		if (res > (x + 1) >> 1)	return 0;
	}
	return 1;
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int T = read(0);
	while (T--) {
		int n = read(0), Xor = 0; ll Total = 0;
		for (int i = 1; i <= n; i++)	A[i] = read(0), Xor ^= A[i], Total += A[i];
		if (Xor) {
			int Max = 0, Cnt = 0;
			for (int i = 1; i <= n; i++)	Max = max(Max, A[i] - (Xor ^ A[i]));
			for (int i = 1; i <= n; i++)	Cnt += (A[i] - (Xor ^ A[i]) == Max);
			printf("%lld %d\n", Total - Max + 1, Cnt);
		}
		else {
			map<int, bool>Flag;
			for (int i = 1; i <= n; i++)
				if (Flag.find((lowbit(A[i]) << 1) - 1) == Flag.end())
					Flag[(lowbit(A[i]) << 1) - 1] = solve((lowbit(A[i]) << 1) - 1, n);
			int Cnt = 0;
			for (int i = 1; i <= n; i++)
				Cnt += Flag[(lowbit(A[i]) << 1) - 1];
			printf("%lld %d\n", Total, Cnt);
		}
	}
	return 0;
}

给定\(m\)个物品的交换方式,可以用任意\(k\times a_i\)个物品\(b_i\)换取\(k\times c_i\)个物品\(d_i\),其中\(k\)可以是任意实数。现求一个损耗参数\(\omega\),这会导致每次交换只能获取\(k\times\omega\times c_i\)个物品\(d_i\),问\(\omega\)最大是多少,可以使得没有物品可以被无限获取


考虑建图,\(b_i\)\(d_i\)连边,边权为\(\frac{c_i}{a_i}\),故原问题可以转化为,求一个最大的\(\omega\),使得所有边权乘上\(\omega\)后,不存在乘积>1的环。

考虑二分答案,Check的时候判断可行性,类似于求负环操作。由于边权乘积会很大,所以我们对边权取对数,则找环变为是否存在正环即可。

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#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 = 1e3, M = 2e3;
const double eps = 1e-7;
int now[N + 10], pre[M + 10], child[M + 10], tot, n, m;
double val[M + 10];
void link(int x, int y, double z) { pre[++tot] = now[x], now[x] = tot, child[tot] = y, val[tot] = z; }
bool SPFA(double omega) {
	vector<int>Cnt(n + 1, 0);
	vector<bool>Vis(n + 1, 0);
	vector<double>Dis(n + 1, 0);
	queue<int>H;
	for (int i = 1; i <= n; i++)
		H.push(i), Vis[i] = 1, Dis[i] = 0, Cnt[i] = 1;
	while (!H.empty()) {
		int Now = H.front(); H.pop();
		for (int p = now[Now]; p; p = pre[p]) {
			int son = child[p];
			if (Dis[son] < Dis[Now] + val[p] + log(omega)) {
				Dis[son] = Dis[Now] + val[p] + log(omega);
				if (!Vis[son]) {
					H.push(son), Vis[son] = 1;
					if (++Cnt[son] > n)	return 0;
				}
			}
		}
		Vis[Now] = 0;
	}
	return 1;
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	n = read(0), m = read(0);
	for (int i = 1; i <= m; i++) {
		int a = read(0), b = read(0), c = read(0), d = read(0);
		link(b, d, log(c) - log(a));
	}
	double l = 0, r = 1;
	while (l <= r) {
		double mid = (l + r) / 2;
		if (SPFA(mid))	l = mid + eps;
		else	r = mid - eps;
	}
	printf("%.10lf\n", r);
	return 0;
}

E.Falfa with Substring

对所有的\(1\leqslant i\leqslant n\),求长度为\(n\)的字符串中恰好出现了\(i\)个"bit"子串的方案数,对998244353取模


容斥 + 多项式,咕咕咕

F.NIO with String Game

给定一个字符串\(s\)\(n\)个匹配串\(t_i\)\(Q\)次操作,有以下四种:

  • 在一个匹配串\(t_i\)末尾加上一个字符\(c\)
  • 删除\(s\)末尾\(p\)个字符
  • \(s\)的末尾增加\(k\)个字符\(c\)
  • 询问有多少个匹配串的字典序严格小于\(s\)

\(n,Q\leqslant 2\times10^5\)


Trie + 线段树 + 倍增,咕咕咕

构造一个排列\(p\),使其max(lis(p),lds(p))最小


大胆猜测一下,例如\(n=9\)时,我们构造一个形如\(3,2,1,6,5,4,9,8,7\)的排列,则其最长上升和最长下降子序列长度皆为3

故我们将其按照\(\sqrt{n}\)分组即可

/*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');
}
int main() {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    int T = read(0);
    while (T--) {
        int n = read(0), Sz = (int)ceil(sqrt(n));
        for (int i = 1; i <= n / Sz; i++)
            for (int j = 1; j <= Sz; j++)
                printf("%d ", i * Sz - j + 1);
        for (int i = 1; i <= n - n / Sz * Sz; i++)
            printf("%d ", n - i + 1);
        putchar('\n');
    }
    return 0;
}

H.Take the Elevator

\(n\)个人坐着电梯,楼高\(m\)层,第\(i\)个人希望从\(a_i\)层到\(b_i\)层。电梯的载客量为\(k\),上升时可以上升到任意高度,但下降必须下降到第1层才能再次上升。电梯每秒运行一层,换方向和上下人不占时间,问电梯最短运行时间


因为每次下行都必须降到第1层,所以我们考虑每次上行的时候尽量往高了跑

具体而言,我们将上行的人看成\([l,r]\)的线段,考虑电梯的两种情况:

  • 人数不足\(k\),选择一条未选择的\(r<r_{\max}\)\(r\)最大的线段加入电梯
  • 人数等于\(k\),选择一条\(r<l_{\max}\)\(r\)最大的线段(相当于他在\(r\)层下电梯,有人在\(l_{\max}\)层上电梯,并不冲突)

下行的时候也是一样的,贪心的选起始楼层高的即可

/*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');
}
struct node {
	int l, r;
	node(int _l = 0, int _r = 0) { l = _l, r = _r; }
	void print() { printf("%d %d\n", l, r); }
};
struct cmpl {
    bool operator()(const node& A, const node& B)const { return A.l > B.l; }
};
struct cmpr {
    bool operator()(const node& A, const node& B)const { return A.r > B.r; }
};
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0), m = read(0), k = read(0);
	UNUSED(k);
        multiset<node, cmpl>temp1, Down;
        multiset<node, cmpr>temp2, Up;
        for (int i = 1; i <= n; i++) {
		int l = read(0), r = read(0);
		if (l < r)	Up.insert(node(l, r));
		else	Down.insert(node(l, r));
	}
	ll Ans = 0;
	while (n) {
		int High = 0;
		auto u = Up.begin(), d = Down.begin();
		if (u == Up.end() || d == Down.end())
			High = u == Up.end() ? d->l : u->r;
		else
			High = max(u->r, d->l);
		Ans += (High - 1) << 1;

		for (int Npos = High, Cnt = 0;;) {
            while (Cnt < m) {
                auto p = Up.lower_bound(node(0, Npos));
				if (p == Up.end())	break;
				++Cnt, --n, Npos = p->r;
				temp1.insert(*p);
				Up.erase(p);
			}
			if (temp1.empty())	break;
			Npos = temp1.begin()->l;
			temp1.erase(temp1.begin()), --Cnt;
        }

        for (int Npos = High, Cnt = 0;;) {
			while (Cnt < m) {
                auto p = Down.lower_bound(node(Npos, 0));
				if (p == Down.end())	break;
				++Cnt, --n, Npos = p->l;
				temp2.insert(*p);
				Down.erase(p);
			}
			if (temp2.empty())	break;
			Npos = temp2.begin()->r;
			temp2.erase(temp2.begin()), --Cnt;
		}
	}
	printf("%lld\n", Ans);
	return 0;
}

I.let fat tension

给定\(X_1,X_2,...,X_n\in \R^k,Y_1,Y_2,...,Y_n\in\R^d\),定义\(le(i,j)=\frac{X_i\cdot X_j}{\vert X_i\vert\vert X_j\vert}\),对\(i\in[1,n]\),求\(Y_i^{new}=\sum\limits_{j=1}^nle(i,j) Y_j\)


首先我们知道\(le(i,j)\)是一个实数故我们可以枚举\(Y_i^{new}\)的第二维,则有

\[Y_{i,o}^{new}=\sum\limits_{j=1}^n\frac{X_i\cdot X_j}{\vert X_i\vert\vert X_j\vert}Y_{j,o} \]

同时,我们知道\(X_i\cdot X_j=\sum\limits_{p=1}^kX_{i,k}\times X_{j,k}\),则原式可写成

\[Y_{i,o}^{new}=\sum\limits_{j=1}^n\sum\limits_{p=1}^k\frac{X_{i,p}X_{j,p}}{\vert X_i\vert\vert X_j\vert}Y_{j,o} \]

我们将其当成矩阵,可以发现\(\frac{X_{i,j}}{\vert X_i\vert}\)是个很明显的行归一化形式,我们记\(X'\)\(X\)按行归一化后的结果,则原式化为

\[Y_{i,o}^{new}=\sum\limits_{j=1}^n\sum\limits_{p=1}^kX'_{i,p}X'_{j,p}Y_{j,o} \]

我们改变枚举顺序,并将第二个矩阵转置可得

\[Y_{i,o}^{new}=\sum\limits_{p=1}^kX'_{i,p}\sum\limits_{j=1}^nX'^T_{p,j}Y_{j,o} \]

这很明显是两个矩阵乘法的形式,先算后两个矩阵相乘,再算前两个矩阵相乘,时间复杂度为\(O(nkd+knd)\),即为\(O(nkd)\)

/*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');
}
struct Matrix {
	int n, m;
	vector<vector<double>>V;
	Matrix(int _n = 0, int _m = 0) {
		n = _n, m = _m;
		V.resize(n);
		for (int i = 0; i < n; i++)
			V[i].resize(m);
		//V.clear();
	}
	void set(int _n, int _m) {
		n = _n, m = _m;
		V.resize(n);
		for (int i = 0; i < n; i++)
			V[i].resize(m);
		V.clear();
	}
	void clear() { V.clear(); }
	Matrix Transform() {
		Matrix ots(m, n);
		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				ots.V[j][i] = V[i][j];
		return ots;
	}
	void print() {
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++)
				printf("%.10lf ", V[i][j]);
			putchar('\n');
		}
	}
};
Matrix operator *(const Matrix& A, const Matrix& B) {
	Matrix C(A.n, B.m);
	for (int i = 0; i < A.n; i++)
		for (int j = 0; j < B.m; j++)
			for (int k = 0; k < A.m; k++)
				C.V[i][j] += A.V[i][k] * B.V[k][j];
	return C;
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0), k = read(0), d = read(0);
	Matrix A(n, k), B(n, d);
	for (int i = 0; i < n; i++) {
		double Total = 0;
		for (int j = 0; j < k; j++) {
			A.V[i][j] = read(0);
			Total += sqr(A.V[i][j]);
		}
		Total = sqrt(Total);
		for (int j = 0; j < k; j++)
			A.V[i][j] /= Total;
	}
	for (int i = 0; i < n; i++)	
		for (int j = 0; j < d; j++)
			B.V[i][j] = read(0);
	Matrix C = A * (A.Transform() * B);
	C.print();
	return 0;
}

给定一个数列\(A\),将其修改为一个等差数列\(B\),代价为\(\sum\limits_{i=1}^n(A_i-B_i)^2\),求最小代价


一次线性回归,套式子即可

/*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;
double y[N + 10];
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int T = read(0);
	while (T--) {
		int n = read(0);
		double xBar = 0, yBar = 0;
		for (int i = 1; i <= n; i++) {
			xBar += i;
			yBar += (y[i] = read(0));
		}
		xBar /= n, yBar /= n;
		double b1 = 0, b2 = 0;
		for (int i = 1; i <= n; i++) {
			b1 += (i - xBar) * (y[i] - yBar);
			b2 += sqr(i - xBar);
		}
		double b = b1 / b2, a = yBar - b * xBar;
		double Ans = 0;
		for (int i = 1; i <= n; i++) {
			double t = b * i + a;
			Ans += sqr(y[i] - t);
		}
		printf("%.10lf\n", Ans);
	}
	return 0;
}

给定长度为\(n\)的括号序列\(a\),已知其为长度为\(m\)的合法括号序列\(b\)的子序列,求可能的\(b\)的方案数


涉及方案数基本上就是DP

\(f[i][j][k]\)表示考虑到\(b\)的第\(i\)位,\(a\)的第\(j\)位,目前左括号比右括号多\(k\)个的方案数

转移时枚举当前填的是哪种括号,并且判断\(a\)的第\(j\)是否为当前所填括号即可(具体转移见代码)

/*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 = 2e2, P = 1e9 + 7;
int F[N + 10][N + 10][N + 10];
char s[N + 10];
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int T = read(0);
	while (T--) {
		memset(F, 0, sizeof(F));
		int n = read(0), m = read(0);
		scanf("%s", s + 1);
		F[0][0][0] = 1;
		for (int i = 1; i <= m; i++) {
			for (int j = 0; j <= i; j++) {
				for (int k = 0; k <= i; k++) {
					if (k) {
						if (j && s[j] == '(')
							F[i][j][k] = (F[i][j][k] + F[i - 1][j - 1][k - 1]) % P;
						else
							F[i][j][k] = (F[i][j][k] + F[i - 1][j][k - 1]) % P;
					}
					if (k < i) {
						if (j && s[j] == ')')
							F[i][j][k] = (F[i][j][k] + F[i - 1][j - 1][k + 1]) % P;
						else
							F[i][j][k] = (F[i][j][k] + F[i - 1][j][k + 1]) % P;
					}
				}
			}
		}
		printf("%d\n", F[m][n][0]);
	}
	return 0;
}

给定\(n\)个世界,每个世界有\(m\)个点,第\(i\)个世界有\(l_i\)条有向边

玩家可以玩一个游戏,从初始世界的1号点开始,每次可以选择经过一次当前世界的有向边,或者什么都不做,之后传送到下一个世界的对应节点上。如果玩家到达了点\(m\),则游戏胜利

现需要从\(n\)个世界中选出连续的一段来生成这样一个游戏,问最少需要多少个世界能保证玩家胜利?


考虑DP,设\(F[i][j]\)表示从之前某个世界的节点1到第\(i\)个世界的节点\(j\)的最小步数,则对第\(i\)个世界而言,若其存在\(u\rightarrow v\)的边,则有\(F[i+1][v]=\min(F[i][v],F[i][u])+1\)

初值\(F[i][1]=0\),最后统计\(F[i][m]\)的最小值即可

(可以用滚动数组优化掉第一维)

/*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 0x3f3f3f3f
#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 = 2e3;
int _F[2][N + 10];
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0), m = read(0), Ans = iMax;
	memset(_F, 63, sizeof(_F));
	for (int i = 1; i <= n; i++) {
		int* F = _F[i & 1];
		int* G = _F[(i - 1) & 1];
		G[1] = 0;
		for (int j = 2; j <= m; j++)
			F[j] = G[j] + 1;
		int k = read(0);
		for (int j = 1; j <= k; j++) {
			int x = read(0), y = read(0);
			F[y] = min(F[y], G[x] + 1);
		}
		Ans = min(Ans, F[m]);
	}
	printf("%d\n", Ans == iMax ? -1 : Ans);
	return 0;
}
posted @ 2022-08-04 10:44  Wolfycz  阅读(36)  评论(0编辑  收藏  举报