The 2021 Sichuan Provincial Collegiate Programming Contest

E.Don’t Really Like How The Story Ends

参考题解

思路

举例:从 1 1 1 开始出发,接下来连接的点要遍历的是 2 2 2,那么接下来的几种情况是

  • ( 2 , 3 ) (2, 3) (2,3) 存在
  • ( 2 , 3 ) (2, 3) (2,3) 不存在,但是 2 2 2 之后没有点和 2 2 2 相连,那么这个节点就没必要遍历了,加上 3 3 3 并不会使结果变好,如果 ( 1 , 3 ) (1,3) (1,3) 存在,那么回溯到 1 1 1,结果最优,如果不存在到时候再说
  • ( 2 , 3 ) (2, 3) (2,3) 不存在,但是 2 2 2 之后有点和 2 2 2 相连,比如有 ( 2 , 4 ) (2,4) (2,4),那么必须把 3 3 3 加入,否则就到了 4 4 4

细节解释

  • 注释1:
  • 注释2:
  • 注释3:
  • 注释4:
  • 注释5:
  • 注释6:
  • 注释7:
  • 注释8:

AC(dfs)

#include <bits/stdc++.h>
using namespace std;
#define PB push_back
typedef vector<int> VI;
const int N = 1e5 + 10;
int n, m, ne, ans;
VI edge[N];
void dfs(int e) {
	if (e == n + 1) return; //注释1
	for (auto x : edge[e]) { //注释2
		while (x >= ne) //注释13
			if (x == ne) { //注释4
				ne ++;
				dfs(ne - 1);
			} else if (x > ne) { //注释5
				ne ++;
				ans ++;
				dfs(ne - 1);
			}
	}
}
int main() {
	int t;
	scanf("%d", &t);
	while (t -- ) {
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= m; i ++ ) {
			int u, v;
			scanf("%d%d", &u, &v);
			edge[u].PB(v), edge[v].PB(u); 
		}
		ne = 2, ans = 0; //注释6
		edge[1].PB(n + 1); //注释7
		for (int i = 1; i <= n; i ++ ) 
			sort(edge[i].begin(), edge[i].end()); //注释8
		dfs(1);
		cout << ans << endl;
		for (int i = 1; i <= n; i ++ ) edge[i].clear();
	}
	return 0;
}

L. Spicy Restaurant

参考博客

时间复杂度

O ( 100 ∗ ( n + m ) ) O(100 * (n + m) ) O(100(n+m))

思路

  • w [ i ] < 100 w[i] < 100 w[i]<100提示
  • 一开始我的思路是在考虑要不要就直接每个点 b f s bfs bfs,算了一下复杂度是 O ( 100 n 2 ) O(100n ^ 2) O(100n2),显然不对,所以就否定了该想法
  • 那么考虑多源 b f s bfs bfs ,时间复杂度就可以降低到 O ( 100 ( n + m ) ) O(100(n + m)) O(100(n+m))
  • 分析时间复杂度:注释1和2就是 100 n 100 n 100n,注释3和4就是 100 m 100m 100m
  • d i s t [ i ] [ j ] dist[i][j] dist[i][j]含义: j j j 是辣度, i i i 是餐馆

细节解释

  • 注释1:对于辣度等于 a a a 的餐馆放入 t m p tmp tmp 队列中
  • 注释2:并且这个辣度这个餐馆是可以不移动的,即 d i s t [ i ] [ j ] dist[i][j] dist[i][j] 0 0 0
  • 注释3:如果 x x x 餐馆的 a a a 辣度没有被更新过,那么就把他放入 t m p tmp tmp 队列里
  • 注释4: 防止反复,所以取 m i n min min,也就是找到目前这个点 d i s t [ t ] [ a ] dist[t][a] dist[t][a] 转移到 d i s t [ x ] [ a ] dist[x][a] dist[x][a] 近,还是 d i s t [ x ] [ a ] dist[x][a] dist[x][a]本身就很近
  • 注释5: w [ i ] < 100 w[i] < 100 w[i]<100提示
  • 注释6:因为在某些辣度 a a a 并没有餐馆

总结

  • 第一次被 cincout

AC(bfs)

#include <bits/stdc++.h>
using namespace std;
#define PB push_back
typedef vector<int> VI;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
int n, m, q;
int w[N];
int dist[N][110];
VI edge[N];
void bfs(int a) {
	queue<int> tmp;
	for (int i = 1; i <= n; i ++ ) 
		if (w[i] == a) {
			tmp.push(i); //注释1
			dist[i][a] = 0; //注释2
		}
	while (!tmp.empty()) {
		int t = tmp.front();
		tmp.pop();
		for (auto x : edge[t]) {
			if (dist[x][a] == INF) tmp.push(x); //注释3
			dist[x][a] = min(dist[x][a], dist[t][a] + 1); //注释4
		}
	}
}
int main() {
	scanf("%d%d%d", &n, &m, &q);
	memset(dist, 0x3f, sizeof dist);
	for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
	for (int i = 1; i <= m; i ++ ) {
		int u, v;
		scanf("%d%d", &u, &v);
		edge[u].PB(v), edge[v].PB(u);
	}
	for (int i = 1; i <= 100; i ++ ) bfs(i); //注释5
	for (int i = 1; i <= n; i ++ ) {
		for (int j = 1; j <= 100; j ++ )
			dist[i][j] = min(dist[i][j], dist[i][j - 1]);
	} //注释6
	for (int i = 1; i <= q; i ++ ) {
		int q, a;
		scanf("%d%d", &q, &a);
		if (dist[q][a] == INF) printf("-1\n");
		else printf("%d\n", dist[q][a]);
	}
	return 0;
}

J.Ants

思路

  • t l tl tl 以左为出口的尾位置, t r tr tr 以右为出口的尾位置, h l hl hl 以左为出口的头位置, h r hr hr 以右为出口的头位置, l l l 就是左为出口的蚂蚁队列, r r r 就是右为出口的蚂蚁队列, t m p tmp tmp 最后一轮,蚂蚁所需要的时间
  • 对于前面的轮次,我们可以直接忽略,我们只要考虑最后一次蚂蚁怎么出去就可以了,把前面的轮次减去,我们可以保证会在下一轮全部出去

细节注释

  • 注释1:对于前面的轮次,我们可以直接忽略,我们只要考虑最后一轮蚂蚁怎么出去就可以了
  • 注释2:两个障碍物的坚硬度减去轮数,注意此时不一定存在 A = = 0 A == 0 A==0 B = = 0 B == 0 B==0
  • 注释3:题干中 a [ i ] < a [ i + 1 ] a[i] < a[i + 1] a[i]<a[i+1],所以对于左边是正序,右边是逆序,对于右来说 a [ i ] a[i] a[i] 越大,他离右端点越近。为什么一定是顺序来,因为蚂蚁撞障碍物有先后顺序
  • 注释4:左右队列只要不是空,因为就是说,还有蚂蚁没出去
  • 注释5:右边的蚂蚁走完了,或者是左边右边都没走完并且左边的蚂蚁比右边的蚂蚁先撞障碍物,另一种情况就是左边走完了,或者左右都没走完并且右边的蚂蚁比左边先撞障碍物
  • 注释6:在注释5条件下,取出当前最快撞障碍物的蚂蚁,和 t m p tmp tmp 比,防止这是一个从左边出去的蚂蚁
  • 注释7:只要左边的障碍物坚硬度不为 0 0 0,就意为着,左边的蚂蚁会撞他,并从右边出去,所以我们要把他加入右边蚂蚁的队列,为什么是 u + m o d u + mod u+mod,对于右就是 l [ i ] + m o d l[i] + mod l[i]+mod,把左转化成右

AC(bf)

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 1;
const int N = 2e6 + 10;
LL l[N], r[N], a[N];
int d[N];
int hl, hr, tl, tr;
LL tmp, A, B;
void judge1() {
	LL u = l[++ hl]; //注释6
	tmp = max(tmp, u); 
	if (A) { //注释7
		A --; //注释7
		r[++ tr] = u + mod; //注释7
	}
}
void judge2() {
	LL u = r[++ hr];
	tmp = max(tmp, u);
	if (B) {
		B --;
		l[++ tl] = u + mod;
	}
}
int main() {
    int n;
    scanf("%d%lld%lld", &n, &A, &B);
    for (int i = 1; i <= n; i ++ ) scanf("%lld", a + i);
    for (int i = 1; i <= n; i ++ ) scanf("%d", d + i);
    LL lun = min(A / (LL)n, B / (LL)n); //注释1
    LL ans = 1ll * lun * mod * 2; 
    A -= lun * n, B -= lun * n; //注释2
    for (int i = 1; i <= n; i ++ ) 
    	if (d[i] == 0) l[++ tl] = a[i]; //注释3
	for (int i = n; i >= 1; i -- ) 
		if (d[i]) r[++ tr] = mod - a[i]; //注释3
	while (hl < tl || hr < tr) { //注释4
		if (hr >= tr || (hl < tl && l[hl] < r[hr])) judge1(); //注释5
		else judge2();
	}
	printf("%lld\n", tmp + ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值