训练赛4题解

这篇博客包含了四道算法竞赛题的解析,涉及亲戚关系的并查集模型、公路路标设置的二分查找策略、芝士原子的电子排布模拟以及FizzBuzz问题的直接判断方法。通过对这些问题的解答,帮助读者理解和解决实际的算法问题。

A.亲戚

题目背景

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

题目描述

规定: x x x y y y 是亲戚, y y y z z z 是亲戚,那么 x x x z z z 也是亲戚。如果 x x x y y y 是亲戚,那么 x x x 的亲戚都是 y y y 的亲戚, y y y 的亲戚也都是 x x x 的亲戚。

输入格式

第一行:三个整数 n , m , p n,m,p n,m,p,( n , m , p ≤ 5000 n,m,p \le 5000 n,m,p5000),分别表示有 n n n 个人, m m m 个亲戚关系,询问 p p p 对亲戚关系。

以下 m m m 行:每行两个数 M i M_i Mi M j M_j Mj 1 ≤ M i ,   M j ≤ N 1 \le M_i,~M_j\le N 1Mi, MjN,表示 M i M_i Mi M j M_j Mj 具有亲戚关系。

接下来 p p p 行:每行两个数 P i , P j P_i,P_j Pi,Pj,询问 P i P_i Pi P j P_j Pj 是否具有亲戚关系。

输出格式

p p p 行,每行一个 YesNo。表示第 i i i 个询问的答案为“具有”或“不具有”亲戚关系。

样例 #1

样例输入 #1

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

样例输出 #1

Yes
Yes
No

这其实是一个很有现实意义的问题。我们可以建立模型,把所有人划分到若干个不相交的集合中,每个集合里的人彼此是亲戚。为了判断两个人是否为亲戚,只需看它们是否属于同一个集合即可。因此,这里就可以考虑用并查集进行维护了。
按秩合并:

int fd(int x)
{
    if (f[x] == x)
        return x;
    else	
        return f[x] = fd(f[x]);
}
void ph(int a, int b)
{
    int x = fd(a), y = fd(b);
    if (r[x] <= r[y])
        f[x] = y;
    else
        f[y] = x;
    if (r[x] == r[y] && x != y)
        r[y]++;

}
int main()
{
    int n, m, p, a, b, i;
    cin >> n >> m >> p;
    for (i = 1; i <= n; i++)
    {
        f[i] = i;
        r[i] = i;
    }
    for (i = 0; i < m; i++)
    {
        cin >> a >> b;
        ph(a, b);
    }
    for (i = 0; i < p; i++)
    {
        cin >> a >> b;
        if (fd(a) == fd(b))     cout << "Yes" << endl;
        else    cout << "No" << endl;
    }
}

B.[TJOI2007]路标设置

题目背景

B 市和 T 市之间有一条长长的高速公路,这条公路的某些地方设有路标,但是大家都感觉路标设得太少了,相邻两个路标之间往往隔着相当长的一段距离。为了便于研究这个问题,我们把公路上相邻路标的最大距离定义为该公路的“空旷指数”。

题目描述

现在政府决定在公路上增设一些路标,使得公路的“空旷指数”最小。他们请求你设计一个程序计算能达到的最小值是多少。请注意,公路的起点和终点保证已设有路标,公路的长度为整数,并且原有路标和新设路标都必须距起点整数个单位距离。

输入格式

1 1 1 行包括三个数 L , N , K L,N,K L,N,K,分别表示公路的长度,原有路标的数量,以及最多可增设的路标数量。

2 2 2 行包括递增排列的 N N N 个整数,分别表示原有的 N N N 个路标的位置。路标的位置用距起点的距离表示,且一定位于区间 [ 0 , L ] [0,L] [0,L] 内。

输出格式

输出 1 1 1 行,包含一个整数,表示增设路标后能达到的最小“空旷指数”值。

样例 #1

样例输入 #1

101 2 1
0 101

样例输出 #1

51

提示

公路原来只在起点和终点处有两个路标,现在允许新增一个路标,应该把新路标设在距起点 50 50 50 51 51 51 个单位距离处,这样能达到最小的空旷指数 51 51 51

50 % 50\% 50% 的数据中, 2 ≤ N ≤ 100 2 \leq N \leq 100 2N100 0 ≤ K ≤ 100 0 \leq K \leq 100 0K100

100 % 100\% 100% 的数据中, 2 ≤ N ≤ 100000 2 \leq N \leq 100000 2N100000, 0 ≤ K ≤ 100000 0 \leq K \leq100000 0K100000

100 % 100\% 100% 的数据中, 0 < L ≤ 10000000 0 < L \leq 10000000 0<L10000000

直接二分答案,加模拟

long long i, l, n , k, ans = 0;
	cin >> l >> n >> k;
	for ( i = 0; i < n; i++)
	{
		cin >> t[i];
	}
	long long le = 0, ri = l+1, mi,x;
	while ( le<=ri)
	{
		x = 0;
		mi = (le + ri) >> 1;
		for ( i = 1; i < n; i++)
		{
			int dist = t[i] - t[i - 1];
			if (dist >= mi)
			{
				x += dist / mi;
				if (dist % mi == 0)
					x--;
			}
		}
		if (x <= k)
		{
			ri = mi - 1;
		}
		else
		{
			le = mi + 1;
		}
	}
	cout << le;

C.芝士原子

题目背景

芝士世界的原子由电子和核子组成,芝士世界的恶霸浩浩想要研究第 n 号元素的电子排布。

电子排布在电子层中,第 i 个电子层有 i 个电子亚层,在本题的数据范围内,你只需要考虑每个电子层最多前 4 个电子亚层,我们命名为 s,p,d,f。每一个电子亚层用"电子层编号+电子亚层字母"来表示。

同时,每一个电子亚层都有一定的容量,每一个电子亚层中填充的电子数不能超过其容量,s 类电子亚层容量为 2,p 类电子亚层容量为 6,d 类电子亚层容量为 10,ff 类电子亚层容量为 14。

例:第 3 个电子层的第 2 个电子亚层为 3p,其容量为 6,第 6 个电子层的第 4 个电子亚层为 6f,其容量为 14。

电子的填充有一定的顺序,如果前一个电子亚层还没有填充满,则不能将电子填入当前电子亚层。在本题数据范围内,电子填充电子亚层的顺序如下。


1s 2s 2p 3s 3p 4s 3d 4p 5s 4d 5p 6s 4f 5d 6p 7s 5f 6d 7p

填充了电子的电子亚层我们以“电子亚层名称+电子数”的格式表示,例如 3p6 表示 3p 电子亚层填入了 6 个电子。

芝士世界有自己的规则,因此芝士世界原子的电子排布不必考虑洪特法则的特例,可参见输入输出样例第 4 组测试数据(对应元素为铜)。如果你不知道这句话的含义,可以忽略,这个不影响完成这道题目。

题目描述

你需要将 n 个电子填充在电子亚层中,每一个电子亚层有一定的容量,每一个电子亚层中填充的电子数不能超过其容量。

电子亚层的命名格式为 xc,其中 x 为一个在 1 至 7 之间的正整数,c 是一个字母,且只可能为 s,p,d,f中的某一个。s 类电子亚层容量为 2,p 类电子亚层容量为 6,d 类电子亚层容量为 10,f 类电子亚层容量为 14。

电子的填充有一定的顺序,如果前一个电子亚层还没有填充满,则不能将电子填入当前电子亚层,电子填充电子亚层的顺序如下。


1s 2s 2p 3s 3p 4s 3d 4p 5s 4d 5p 6s 4f 5d 6p 7s 5f 6d 7p

填充了电子的电子亚层我们以 xcn 的格式表示,其中 xc 表示电子亚层名称,n 表示该电子亚层填入的电子个数。

你需要按照上面的格式输出这 n 个电子的排布,并要求优先输出电子层编号小的,电子层编号相同按照 s,p,d,f 的顺序输出。

输入格式

每个测试点中包含 T 组测试数据。

输入共 T+1 行。

输入的一行为一个正整数 T(1≤T≤200),代表该测试点中测试数据的组数。

接下来 T 行,每行为一个正整数 n(1≤n≤118),代表该组测试数据询问的元素为第 n 号元素

输出格式

输出共 T 行,每行输出所问元素的核外电子排布式。

要求优先输出电子层小的,电子层相同按照 spdf 的顺序输出。

样例 #1

样例输入 #1

5
1
11
21
29
87

样例输出 #1

1s1
1s2 2s2 2p6 3s1
1s2 2s2 2p6 3s2 3p6 3d1 4s2
1s2 2s2 2p6 3s2 3p6 3d9 4s2
1s2 2s2 2p6 3s2 3p6 3d10 4s2 4p6 4d10 4f14 5s2 5p6 5d10 6s2 6p6 7s1

鉴定为纯纯的模拟

题面长度弥补了题目难度 ——s7win99

map<char, int> a;
string s = "1s2s2p3s3p4s3d4p5s4d5p6s4f5d6p7s5f6d7p";
struct ele
{
	char x;
	char c;
	int n;
};

bool cmp(ele q, ele b)
{
	if (q.x != b.x)
		return q.x < b.x;
	else
	{
		if (a[q.c] < a[b.c])
		{
			return a[q.c] < a[b.c];
		}
		else
			return false;
	}
}
void solve()
{
	int n, cnt = 0;
	cin >> n;
	ele e[20];
	for (int i = 0; i < s.length(); i++)
	{
		if (s[i] >= '1' && s[i] <= '7')
		{
			if (n > a[s[i + 1]])
			{
				e[cnt].x = s[i];
				e[cnt].c = s[i + 1];
				e[cnt].n = a[s[i + 1]];
				cnt++;
				n -= a[s[i + 1]];
			}
			else
			{
				e[cnt].x = s[i];
				e[cnt].c = s[i + 1];
				e[cnt].n = n;
				break;
			}
		}
	}
	sort(e, e + cnt + 1, cmp);
	for (int i = 0; i <= cnt; i++)
	{
		cout << e[i].x << e[i].c << e[i].n << " ";
	}
	cout << endl;
}

int main()
{
	int n;
	cin >> n;
	a.insert({ 's', 2 });
	a.insert({ 'p', 6 });
	a.insert({ 'd',10 });
	a.insert({ 'f',14 });
	while (n--)
	{
		solve();
	}
}

D.[ABC006A] 世界のFizzBuzz

题面翻译

输入一个整数n,如果n能整除3,或者n的数位中有3,则输出“YES”,否则输出“NO”。

读入n 后直接判断是否为3 的倍数。
如果是,输出并return
如果不是,分离整数的各个数位,看是不是3 ,最后输出YES或NO。
可以用一个while循环一直除,除到除不下去为止。

cin>>n;
y=n;
if(n%3==0)
{
	cout<<"YES"<<endl;
	return 0;
}
else
{
	while(x>0)
	{
		x=y%10;
		y=y/10;
		if(x==3) z=true;
	}
}
if(z==false) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;

E.[ABC151E] Max-Min Sums

题面翻译

给你一个整数集合 A A A , ∣ A ∣ = n |A|=n A=n 和一个数 k k k

∑ S ∈ A [ ∣ S ∣ = k ] f ( S ) \sum\limits_{S\in A}[|S|=k]f(S) SA[S=k]f(S) ,取模 1000000007 1000000007 1000000007

其中 f ( S ) = max ⁡ x ∈ S x − min ⁡ x ∈ S x f(S)=\max\limits_{x\in S}x-\min\limits_{x\in S}x f(S)=xSmaxxxSminx

1 ≤ k ≤ n ≤ 100000 , ∀ x ∈ A , ∣ x ∣ ≤ 1000000000 1\le k\le n\le100000,\forall x\in A,|x|\le1000000000 1kn100000,xA,x1000000000

TonyYin的题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值