1. 程式人生 > 其它 >2021.10.16CSP模擬賽22 賽後總結

2021.10.16CSP模擬賽22 賽後總結

A. 區間

這題似乎用 \(ST\) 表,單調棧等各種方法都可以過。

我用的是無腦線段樹(智商不夠,資料結構來湊)。

簡單來說,維護一下區間最小值及其位置即可,然後遞迴輸出。

直接貼程式碼吧。

這個 \(pushup\) 好像不能寫三目運算子(不知道為什麼),大樣例一直過不去,然後調了半天 \(QwQ\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define ls rt << 1
#define rs rt << 1 | 1

using namespace std;

const int N = 2e5 + 10;
int n;
int a[N], mins[N << 2], pos[N << 2];

inline void pushup(int rt){
	if(mins[ls] <= mins[rs]) pos[rt] = pos[ls], mins[rt] = mins[ls];
	else pos[rt] = pos[rs], mins[rt] = mins[rs];
}

inline void build(int l, int r, int rt){
	if(l == r){
		mins[rt] = a[l];
		pos[rt] = l;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, ls);
	build(mid + 1, r, rs);
	pushup(rt);
}

inline int query(int L, int R, int l, int r, int rt){
	if(l > R || r < L) return 0;
	if(L <= l && r <= R)
		return pos[rt];
	int mid = (l + r) >> 1;
	int res = 0;
	if(L <= mid) res = query(L, R, l, mid, ls);
	if(R > mid){
		int id = query(L, R, mid + 1, r, rs);
		if(a[id] < a[res]) res = id;
	}
	return res;
}

struct node{
	int l, r;
}ans[N];
int cnt;

inline void dfs(int l, int r, int tmp){
	if(l > r) return;
	int id = query(l, r, 1, n, 1);
	int res = a[id] - tmp, d = 0;
	while(res > 0){
		ans[++cnt].l = l, ans[cnt].r = r;
		res--, d++;
	}
	dfs(l, id - 1, tmp + d), dfs(id + 1, r, tmp + d);
}

int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	a[0] = 1e9;
	memset(mins, 0x3f, sizeof(mins));
	build(1, n, 1);
	dfs(1, n, 0);
	printf("%d\n", cnt);
	for(int i = 1; i <= cnt; i++)
		printf("%d %d\n", ans[i].l, ans[i].r);
	return 0;
}

B. 樹

事實上我並不是很會算期望,所以就算給我 \(n \leq 10\) 資料的我也做不出來 \(QwQ\)

考場上當然就直接棄了。

後來聽學長講,發現是一波大力推式子。

由於輸入的是一棵樹,所以從 \(u\)\(v\) 的路徑是先網上走到 \(lca(u, v)\),再往下走到 \(v\)

所以先推出來從 \(x\) 節點走到 \(fa_x\) 的期望,然後推從 \(fa_x\) 走到 \(x\) 的期望(懶得打了,可以去看別人的題解)。就可以求解了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define ll long long

using namespace std;

inline int read(){
	int x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}

const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, m;
struct node{
	int v, nxt;
}edge[N << 1];
int head[N], tot;
int ans;
int fa[N][20], dep[N];
ll f[N],g[N];

inline void add(int x, int y){
	edge[++tot] = (node){y, head[x]};
	head[x] = tot;
}

inline void dfs1(int x, int p){
	fa[x][0] = p, dep[x] = dep[p] + 1;
	for(int i = 1; i <= 20; i++)
		fa[x][i] = fa[fa[x][i - 1]][i - 1];
	f[x] = 1;
	for(int i = head[x]; i; i = edge[i].nxt){
		int y = edge[i].v;
		if(y == p) continue;
		dfs1(y, x);
		f[x] = (f[x] + f[y] + 1) % mod;
	}
}

inline void dfs2(int x, int p){
	for(int i = head[x]; i; i = edge[i].nxt){
		int y = edge[i].v;
		if(y == p) continue;
		g[y] = (g[x] + f[x] - f[y]) % mod;
		dfs2(y, x);
	}
}

inline void dfs3(int x, int p){
	f[x] += f[p], g[x] += g[p];
	for(int i = head[x]; i; i = edge[i].nxt)
		if(edge[i].v != p)
			dfs3(edge[i].v, x);
}

inline int lca(int a, int b){
	if(dep[a] < dep[b]) swap(a, b);
	for(int i = 20; i >= 0; i--)
		if(dep[fa[a][i]] >= dep[b]) a = fa[a][i];
	if(a == b) return a;
	for(int i = 20; i >= 0; i--)
		if(fa[a][i] != fa[b][i])
			a = fa[a][i], b = fa[b][i];
	return fa[a][0];
}

signed main(){
	n = read(), m = read();
	for(int i = 1; i < n; i++){
		int u = read(), v = read();
		add(u, v), add(v, u);
	}
	dfs1(1, 0);
	f[1]--;
	dfs2(1, 0), dfs3(1, 0);
	while(m--){
		int u = read(), v = read();
		int k = lca(u, v);
		// cout <<" dd " <<k << endl;
		printf("%lld\n", ((f[u] - f[k] + g[v] - g[k]) % mod + mod) % mod);
	}
	return 0;
}

C. 做運動

巨水的一道題,但是高一學生們全都寫的二分 + \(dij\)默契十足),複雜度 \(O(nlog_n^2)\),然後全都被卡到 10 ~ 30 分不等……

然鵝正解是並查集 + \(dij\),複雜度 \(O(nlogn)\)

先按溫度排序,然後不停加邊,當起點和終點在同一集合中時,最大溫度就是那條邊的溫度,然後把所有合法的邊全都加進去,跑個最短路即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#define INF 1e18
#define ll long long

using namespace std;

inline ll read(){
	ll x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}

const ll N = 5e5 + 10;
const ll M = 1e6 + 10;
ll n, m;
struct node{
	ll u, v, t, c, nxt;
	bool operator < (const node &b) const{
		return t < b.t;
	}
}edge[M << 1], e[M];
ll head[N], tot;
ll temp[M];
ll st, en;
ll dis[N], f[N];
struct Que{
	ll x, dis;
	bool operator < (const Que &b) const{
		return dis > b.dis;
	}
};

inline void add(ll x, ll y, ll c){
	edge[++tot].v = y, edge[tot].c = c, edge[tot].nxt = head[x];
	head[x] = tot;
}

inline void dijkstra(){
	priority_queue <Que> q;
	q.push((Que){st, 0});
	for(int i = 1; i <= n; i++) dis[i] = INF;
	dis[st] = 0;
	while(!q.empty()){
		Que now = q.top();
		q.pop();
		ll x = now.x;
		if(dis[x] < now.dis) continue;
		for(ll i = head[x]; i; i = edge[i].nxt){
			ll y = edge[i].v;
			if(dis[y] > dis[x] + edge[i].c){
				dis[y] = dis[x] + edge[i].c;
				q.push((Que){y, dis[y]});
			}
		}
	}
}

inline int find(int x){
	return f[x] == x ? x : f[x] = find(f[x]);
}

signed main(){
	n = read(), m = read();
	for(ll i = 1; i <= m; i++){
		e[i].u = read(), e[i].v = read(), e[i].t = read(), e[i].c = read();
	}
	st = read(), en = read();
	for(int i = 1; i <= n; i++)
		f[i] = i;
	sort(e + 1, e + 1 + m);
	ll mint, pos;
	for(int i = 1; i <= m; i++){
		int fu = find(e[i].u), fv = find(e[i].v);
		if(fu != fv) f[fu] = fv;
		add(e[i].u, e[i].v, e[i].c * e[i].t), add(e[i].v, e[i].u, e[i].c * e[i].t);
		if(find(st) == find(en)){
			mint = e[i].t, pos = i;
			break;
		}
	}
	for(int i = pos + 1; i <= m; i++){
		if(e[i].t != e[pos].t) break;
		add(e[i].u, e[i].v, e[i].c * e[i].t), add(e[i].v, e[i].u, e[i].c * e[i].t);
	}
	dijkstra();
	printf("%lld %lld\n", mint, dis[en]);
	return 0;
}

D. 手機訊號

考場上打 \(O(n^2)\) 暴力,結果沒注意寫了個 \(c * c\) 直接 \(1e36\)\(long\ long\) 了,然後 30pts 都沒有拿到 \(QwQ\)

正解似乎是 \(set\) + 亂搞。

然鵝還不太會,先咕了,以後會了在補吧。

本文來自部落格園,作者:{xixike},轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15421858.html