EOJ 2021.2 月赛 (先挖坑)

待补:

A (等我学完LCA)

B (未

C (未


传送门

官方题解传送门

哈哈,IOI赛制,老部分分选手 119/400 谢罪


4小时真的打吐了

先吐槽一波A,我47分和之前有个7分的代码就只是删了个注释,这时间卡的,(当然也有可能是我自己的常数太大,只能说还好不罚时不然我炸开

感觉很难取舍,第一眼看都没思路,于是几乎是一档一档打的,就导致后来看到x题xx分能出再去想再去做有点没时间(是非常没时间可恶


A 昔我往矣

正解LCA,树链剖分

快去学ye不能总靠队友,可恶

47分算法( q ≤ 100 q\leq 100 q100

因为保证是一棵树,所以任意两点间的路径是唯一的,

五个点分别设为abcde,bfs a到其他四个点,把边不重复地加一下就好了,感觉常数可能有点大是卡评测姬过去的(大概

哦又试了一遍是47,好怪(

#include <bits/stdc++.h>
using namespace std;

const int N = 50010;

vector<int> g[N];
unordered_map<int, int> mp;
int mark[N], p[N];
bool vis[N],haveAdd[N];
int ans, n;

inline void bfs(int s) {
	queue<int> que;
	memset(vis, 0, sizeof vis);
	memset(haveAdd, 0, sizeof haveAdd);
	
	que.push(s);
	vis[s] = 1;
	
	while(!que.empty()) {
		int tmp = que.front();
		que.pop();
		
		int siz = g[tmp].size();
		for(int i = 0; i < siz; ++ i) {
			int u = g[tmp][i];
			if(vis[u]) continue;
			
			vis[u] = 1;
			p[u] = tmp;
			if(mark[u]) {
				//getPath(u);
				int mn, mx;
				int q = u;
				while(1) {
					//se.push_back(q);
					mx = max(q, p[q]);
					mn = min(q, p[q]);
					ans += mp[mx * n + mn]; 
					haveAdd[q] = 1;
					q = p[q];
					if(q == s) break; 
					if(haveAdd[q]) break;
				}
			}
			que.push(u);
		}
	}
}


int main() {
	scanf("%d", &n);
	for(int i = 0, u, v, w; i < n - 1; ++ i) {
		scanf("%d%d%d", &u, &v, &w);
		g[u].push_back(v);
		g[v].push_back(u);
		
		int mn = min(u, v);
		int mx = max(u, v);
		mp[mx * n + mn] = w;
	}
	
	int q;
	scanf("%d", &q);
	while(q--) {
		ans = 0;
		int a, b, c, d, e;
		scanf("%d%d%d%d%d", &a, &b, &c, &d, &e);
		mark[b] = mark[c] = mark[d] = mark[e] = 1;
		bfs(a);
		printf("%d\n", ans);
		mark[b] = mark[c] = mark[d] = mark[e] = 0;
	}
} 

B 杨柳依依

以每个点为起点做一遍 bfs,记录两两点对之间的最短路径长度和数量,可以通过。

待补

38分算法(是树)

因为保证是一棵树,所以任意两点间的路径是唯一的,

就暴力就行了

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
vector<int> g[N];
int cnt[N], v[N];

bool flag = 0;

void dfs(int p, int nw, int t, int idx) {
	if(flag) return ;
	v[idx] = nw;
	if(nw == t) {
		for(int i = 1; i <= idx; ++ i) {
			cnt[v[i]] ++;
		}
		flag = 1;
		return;
	}
	
	for(int i = 0; i < g[nw].size(); ++ i) {
		int tmp = g[nw][i];
		if(tmp == p) continue;
		dfs(nw, tmp, t, idx + 1);
	}
}

int main() {
	int n, m;
	cin >> n >> m;
	for(int i = 0; i < m; ++ i) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	
	int k;
	cin >> k;
	for(int i = 0; i < k; ++ i) {
		int s, t;
		cin >> s >> t;
		//memset(vis, 0, sizeof vis);
		flag = 0;
		dfs(-1, s, t, 1);
	}
	
	int mx = 0, ans = -1;
	for(int i = 0; i <= n; ++ i) {
//		cout << cnt[i] << " " ;
		if(cnt[i] > mx) {
			mx = cnt[i];
			ans = i;
		}
	}
	cout << ans << endl;
	
} 

C 今我来思

23分

直接dfs所有可能性然后判断是否合法就行

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
int n , q;

int least[N], ans[N];
vector<int> g[N];

struct node{
	int l, r, w;
}mp[N];

bool check() {
	for(int i = 1;i <= q; ++ i) {
		bool f = 0;
		for(int j = mp[i].l; j <= mp[i].r; ++ j) {
			if(ans[j] < mp[i].w) return 0;
			if(ans[j] == mp[i].w) f = 1;
		}
		if(f == 0) return 0;
	}
	return 1;
}

int main() {
	cin >> n >> q;
	for(int i = 1; i <= q; ++ i) {
		int l, r, w;
		cin >> l >> r >> w;
		mp[i].l = l; mp[i].r = r; mp[i].w = w;
		for(int j = l; j <= r; ++ j) {
			least[j] = max(least[j], w);
		}
	}
	
	for(int i = 0; i < n; ++ i) {
		g[least[i]].push_back(i);
	}
	bool f = 0;
	int sum = 0;
	for(int i = 0; i < n; ++ i) {
		sum += g[i].size();
		if(sum <= i) {
			f = 1;
			break;
		} 
	}
	
	if(f) {
		for(int i = 1; i <= n; ++ i) {
			cout << -1 << " ";
		}
		return 0;
	}
	int idx = 0;
	for(int i = 0; i < n; ++ i) {
		int siz = g[i].size();
		for(int j = 0; j < siz; ++ j) {
			ans[g[i][j]] = idx++;
		}
	}
	
	if(!check()) {
		for(int i = 1; i <= n; ++ i) {
			cout << -1 << " ";
		}
		return 0;
	}
	for(int i = 0; i < n; ++ i) {
		cout << ans[i] << " ";
	}
	
	
	return 0;
} 

67分,

  1. 维护 0 ~ n − 1 每个数字可能出现范围 [ l , r ] ;
  2. 维护数组 a(即排列 p)中每个元素 $a_i $的最小值。

快结束的时候想冲67,然后因为真的没时间,就写了2,然后第一点卡住了,知道自己会错并且有在想怎么改但是没时间le,


D 雨雪霏霏

看了眼正解,好的不是我的题下一个(

快学莫队啊啊啊

11分暴力

#include <bits/stdc++.h>
using namespace std;

const int N = 50010;
int L[N][2], C[N][2];
bool vis[N];

int main() {
	int r, c, q;
	scanf("%d%d%d", &r, &c, &q);
	for(int i = 1; i <= r; ++ i) {
		for(int j = 1; j <= c; ++ j) {
			scanf("%d", &L[j][i]);
		}
	}
	for(int i = 1; i <= r; ++ i) {
		for(int j = 1; j <= c; ++ j) {
			scanf("%d", &C[j][i]);
		}
	}
	int op, x, y, num;
	while(q--) {
		scanf("%d%d%d%d", &op, &x, &y, &num);
		if(op == 1) {
			C[x][y] = num;
		} 
		else {
			memset(vis, 0, sizeof vis);
			if(num < L[x][y]) {
				cout << 0 << endl;
				continue;
			}
			int ans = 0;
			num = min(num, c);
			for(int i = num; i >= 1; --i) {
				if(!vis[C[i][1]]) {
					ans ++;
					vis[C[i][1]] = 1;
				}
			}
			printf("%d\n", ans);
		}
	} 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值