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

A.Villages: Landlin

数轴上有1个发电站和n-1个建筑,发电站位于\(x_s\)位置,能够与距离\(r_s\)以内的建筑相连。第\(i\)个建筑位于\(x_i\),能与距离\(r_i\)以内的电线杆直接相连。电线杆之间相连需要使用电线,问最少需要多长的电线可以使所有建筑都有能源?

(注意建筑可以传递能量)

这题相当于有n个区间\([x_s-r_s,x_s+r_s],[x_i-r_i,x_i+r_i]\),若区间交集非空则相交。问使得区间全部相交所需要添加的最小区间长度。我们将数据读入,按左端点排序,扫描时不断更新右端点,需要添加区间时更新答案即可。

/*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 = 2e5;
struct Segment {
	int l, r;
	Segment(int _l = 0, int _r = 0) { l = _l, r = _r; }
	bool operator<(const Segment& ots)const { return l < ots.l; }
};
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0);
	vector<Segment>segments;
	for (int i = 1; i <= n; i++) {
		int x = read(0), radius = read(0);
		segments.push_back(Segment(x - radius, x + radius));
	}
	sort(segments.begin(), segments.end());
	ll Ans = 0, MaxR = -lMax;
	bool Flag = 0;
	for (auto segment : segments) {
		if (Flag && MaxR < segment.l)
			Ans += segment.l - MaxR;
		MaxR = max(MaxR, 1ll * segment.r), Flag |= 1;
	}
	printf("%lld\n", Ans);
	return 0;
}

B.Spirit Circle Observation

给定长度为\(n\)的字符串s,求满足条件的二元组(A,B)的个数

  • A,B是s的子串
  • A,B长度相同
  • A+1=B(数字意义上)

即使两个子串相同,只要它们的位置不同便认为是不同子串

\(1\leqslant n\leqslant 4\times 10^5\)

字符串难难,咕咕咕

C.Grab the Seat!

有一个二维平面,屏幕是线段\((0,1)\sim (0,m)\),有\(n\times m\)个座位,有\(k\)个人在座位上,第\(i\)个人坐标为\((x_i,y_i)\)。有\(q\)次操作,每次修改一个人的坐标后,求到屏幕视线不被任何人挡住的座位个数

对于每个人而言,他们所挡住的位置是起点为(0,1),(0,m)的两条射线在该点处相交后之间的区域。如果我们对于每个点求出这些挡住区域的并集即可求出答案。

这样的区域实际上构成了一个折线图,对于每个\(y\)都存在一个分界点\(t\),满足\(x\in[1,t)\)是合法的,\(x\in[t,n]\)则是非法的。

考虑如何维护这样一个折线图,实际上每条折线线段的延长线都是经过屏幕两端的,故我们将其分成两类,如果按照\(y\)降序或者升序依次加入点,不难发现,一旦高斜率的线段加入,便会碾压低斜率的线段,故我们对\(y\)枚举一遍,维护最大斜率即可

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#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 = 2e5;
unordered_set<int>points[N + 10];
int Row[N + 10];
pii A[N + 10];
bool Check(pii A, pii B) { return 1ll * B.second * A.first > 1ll * A.second * B.first; }
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int n = read(0), m = read(0), k = read(0), q = read(0);
	for (int i = 1; i <= k; i++) {
		int x = read(0), y = read(0);
		points[y].insert(x);
		A[i] = MK(x, y);
	}
	for (int i = 1; i <= q; i++) {
		int p = read(0);
		points[A[p].second].erase(A[p].first);
		int x = read(0), y = read(0);
		A[p] = MK(x, y);
		points[y].insert(x);
		pii Now = MK(0, 0);
		for (int j = 1; j <= m; j++) {
			if (j == 1) {
				Row[j] = n + 1;
				for (auto p : points[j])
					Row[j] = min(Row[j], p);
				continue;
			}
			for (auto p : points[j])
				if (!Now.second || Check(Now, MK(p, j - 1)))
					Now = MK(p, j - 1);
			Row[j] = !Now.second ? n + 1 : (int)ceil(1.0 * (j - 1) * Now.first / Now.second);
		}

		Now = MK(0, 0);
		for (int j = m; j >= 1; j--) {
			if (j == m) {
				Row[j] = min(Row[j], n + 1);
				for (auto p : points[j])
					Row[j] = min(Row[j], p);
				continue;
			}
			for (auto p : points[j])
				if (!Now.second || Check(Now, MK(p, m - j)))
					Now = MK(p, m - j);
			Row[j] = min(Row[j], min(!Now.second ? n + 1 : (int)ceil(1.0 * (m - j) * Now.first / Now.second), n + 1));
		}

		ll Ans = 0;
		for (int j = 1; j <= m; j++)
			Ans += Row[j] - 1;
		printf("%lld\n", Ans);
	}
	return 0;
}

D.Mocha and Railgun

给定一个圆和严格圆内一点\(p\),以\(p\)为中点发射长度为\(2d\)的电磁炮,已知\(p\)到圆周距离严格小于\(d\),问最大能摧毁的圆弧长度?

几何题,手画后不难发现,当发射基座位于线段\(Op\)上时,可以摧毁的圆弧最长

/*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--) {
		double radius = read(0), x = read(0), y = read(0), d = read(0);
		double distance = sqrt(sqr(x) + sqr(y));
		double alpha = acos((distance - d) / radius);
		double beta = acos((distance + d) / radius);
		double len = (alpha - beta) * radius;
		printf("%.10lf\n", len);
	}
	return 0;
}

E.LTCS

定义一棵带点权的有根树\(T_a\)\(T_b\)的子序列当且仅当:

  • \(T_a\)的点到\(T_b\)的点存在一个单射\(F\)
  • 对于\(T_a\)中所有的点有\(c_{a,u}=c_{b,F(u)}\)
  • 如果\(u\)\(T_a\)中是\(v\)的祖先,则\(F(u)\)\(T_b\)中是\(F(v)\)的祖先
  • 如果\(u\)\(T_a\)中是\(v,w\)的父亲,则在\(T_b\)\(F(u)\)\(F(v),F(w)\)的简单路径上

求两棵以1为根的树\(T_1,T_2\)的最大公共子序列的大小

dp+二分图匹配,咕咕咕

F.Cut

给定1到\(n\)的排列\(a_1,a_2,...,a_n\),有\(m\)次以下三种操作:

  • \(a_l,a_{l+1},...,a_r\)升序排序
  • \(a_l,a_{l+1},...,a_r\)降序排序
  • 询问\(a_l,a_{l+1},...,a_r\)中最长的奇偶交替子序列

\(1\leqslant n,m\leqslant 10^5\)

线段树合并,咕咕咕

G.Lexicographical Maximum

给定\(n\),求\(1\sim n\)中字典序最大的字符串

显然,答案除去最后一位则全为9(也可能为空)。故对\(n\)进行判断,若\(n\)除去最后一位均为9,则输出\(n\);否则输出\(|n|-1\)个9

(代码略)

H.Fly

\(n\)种物品每种有无限个,第\(i\)个物品的体积为\(a_i\),求解选择物品总体积不超过\(M\)的方案数

此外存在\(k\)个限制,第\(i\)个限制形如第\(b_i\)个物品的所选数量二进制表示从高到低的低\(c_i\)位必须为0

\(1\leqslant n,a_i,\sum a_i\leqslant 10^4\),\(0\leqslant M\leqslant 10^{18}\),\(1\leqslant k\leqslant 5\times 10^3\)

DP + 多项式,咕咕咕

I.Chiitoitsu

初始有13张麻将牌,相同牌至多出现两次。每次从牌堆摸牌,若凑成七对子可直接胡牌,否则将其替换手里的任意一张牌或者直接打出。问在最优策略下,将手牌摸成七对子的期望轮数

首先明确一点,用摸到的牌替换手里的牌一定不会使答案更优,除非手里已经有一张一样的牌;其次,一旦有一对相同的牌在手中,我们边不再动它,故实际上摸牌轮数是由手中的单牌数决定的

此外,在最优策略下,手中的单牌必定在牌堆中还存有3个

考虑DP,设\(F[i][j]\)表示手中有\(i\)个单牌,牌堆还剩\(j\)张牌,将手中的牌摸成对子所需要的期望轮数,则转移方程为:

\[F[i][j]=\frac{3i}{j}\times F[i-2][j-1]+\frac{j-3i}{j}\times F[i][j-1]+1 \]

前者为摸到手中存在的单牌的情况,后者为没有摸到手中单牌的情况。注意当手上仅剩一张牌时,前面的状态就不需要考虑了

最后我们输出\(F[c][136-13]\)即可,\(c\)为初始手中的单牌数量

/*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 = 34, P = 1e9 + 7;
int F[N + 10][(N << 2) + 10], Inv[(N << 2) + 10];
void prepare() {
	Inv[0] = Inv[1] = 1;
	for (int i = 2; i <= N << 2; i++)	Inv[i] = 1ll * (P - P / i) * Inv[P % i] % P;
	//for (int i = 1; i <= N << 2; i++)	F[0][i] = 1;
	for (int i = 1; i <= N; i += 2) {
		for (int j = i * 3; j <= N << 2; j++) {
			int temp = i == 1 ? 0 : F[i - 2][j - 1];
			F[i][j] = 3ll * i % P * Inv[j] % P * temp % P;
			if (j > i * 3)
				F[i][j] = (F[i][j] + 1ll * (j - 3 * i) * Inv[j] % P * F[i][j - 1] % P) % P;
			(++F[i][j]) %= P;
		}
	}
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	prepare();
	int T = read(0);
	for (int Time = 1; Time <= T; Time++) {
		printf("Case #%d: ", Time);
		char s[30];
		scanf("%s", s);
		int len = strlen(s);
		map<string, int>msi;
		for (int i = 0; i < len; i += 2) {
			string temp;
			temp += s[i], temp += s[i + 1];
			msi[temp]++;
		}
		int Cnt = 0;
		for (auto p : msi)
			Cnt += p.second == 1;
		printf("%d\n", F[Cnt][(N << 2) - 13]);
	}
	return 0;
}

J.Serval and Essay

给定一张\(n\)个点\(m\)条边的无重边无自环的有向图,初始可选择一个点染黑,若一个点的入点全都染黑则该点也可以染黑,求最大化黑点数

考虑通过\(x\)确定\(y\)这种关系将\(x,y\)合并,如果\(x\)能够将\(y\)染色,那么我们就不必再考虑\(y\)了,将所有\(y\)指出的边改为\(x\)指出即可

\(x,y\)合并后,原本\(y\)指向的边就变成了\(x\)指向,如果两者指向的边在\(t\)处重合,便会合并出很多\(x\ \mathrm{to}\ t\)的边,我们维护一个set用以自动去重;此外,这个时候的\(t\)也是被\(x\)所决定的,因此我们若在合并\(y\)时出现这种情况,\(t\)也是需要继续合并的

合并次数显然是\(O(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 = 2e5;
int Fa[N + 10], Sz[N + 10];
set<int>Frm[N + 10], Nxt[N + 10];
int Find(int x) { return x == Fa[x] ? x : Fa[x] = Find(Fa[x]); }
void Merge(int x, int y) {
	x = Find(x), y = Find(y);
	if (x == y)	return;
	if (Nxt[x].size() < Nxt[y].size())	swap(x, y);
	Sz[Fa[y] = x] += Sz[y];
	vector<pair<int, int>>temp;
	for (auto p : Nxt[y]) {
		Nxt[x].insert(p);
		Frm[p].erase(y);
		Frm[p].insert(x);
		if (Frm[p].size() == 1)
			temp.push_back(make_pair(x, p));
	}
	for (auto [x, y] : temp)
		Merge(x, y);
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int T = read(0);
	for (int Case = 1; Case <= T; Case++) {
		int n = read(0);
		for (int i = 1; i <= n; i++)	Sz[Fa[i] = i] = 1;
		for (int i = 1; i <= n; i++) {
			int k = read(0);
			for (int j = 1; j <= k; j++) {
				int x = read(0);
				Frm[i].insert(x);
				Nxt[x].insert(i);
			}
		}
		for (int i = 1; i <= n; i++)
			if (Frm[i].size() == 1)
				Merge(*Frm[i].begin(), i);
		int Ans = 0;
		for (int i = 1; i <= n; i++) {
			Ans = max(Ans, Sz[i]);
			Frm[i].clear();
			Nxt[i].clear();
		}
		printf("Case #%d: %d\n", Case, Ans);
	}
	return 0;
}

K.Villages: Landcircles

给定二维平面\(n\)个圆,可在圆外任意处添加额外的点,连接点与点,点与圆,圆与圆的花费为所连线段长度,询问将\(n\)个圆连通的最小费用

防AK题,神仙知识,咕咕咕咕咕咕

posted @ 2022-08-02 01:30  Wolfycz  阅读(59)  评论(0编辑  收藏  举报