2021csp 赛前第五次集训 - 树专题

2014年提高组D1T2 联合权值
  • 时间复杂度O(n^3),30分
#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 2e3 + 10, INF = 0x3f3f3f3f, MOD = 10007;
int n;
LL e[N][N], w[N];
LL maxx, sum;

int main() {
	scanf("%d", &n);
	memset(e, 0x3f, sizeof(e));
	
	for (int i = 1; i < n; i ++) {
		int u, v;
		scanf("%d%d", &u, &v);
		e[u][v] = e[v][u] = 1;
	}
	for (int i = 1; i <= n; i ++) scanf("%lld", &w[i]);

	for (int k = 1; k <= n; k ++) {
		for (int i = 1; i <= n; i ++) {
			for (int j = 1; j <= n; j ++) {
				if (i != j && j != k && k != i) {
					e[i][j] = min(e[i][j], e[i][k] + e[k][j]);
				}
			}
		}
	}

	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= n; j ++) {
			if (e[i][j] == 2) {
				LL x = w[i] * w[j];
				maxx = max(maxx, x);
				sum = (sum + x) % MOD;
			}
		}
	}
	printf("%lld %lld", maxx, sum);

	return 0;
}
  • 时间复杂度O(n),100分
#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 10007;
int n, u[N], v[N];
LL w[N], sumA[N], sumB[N], m1[N], m2[N];	//权值,和,平方和,最大,次大
LL maxx, sum;

void link(int x, int y) {
	sumA[x] = (sumA[x] + w[y]) % MOD;
	sumB[x] = (sumB[x] + w[y] * w[y]) % MOD;
	if (w[y] > m1[x]) m2[x] = m1[x], m1[x] = w[y];
	else if (w[y] > m2[x]) m2[x] = w[y];
}

int main() {
	scanf("%d", &n);

	for (int i = 1; i < n; i ++) scanf("%d%d", &u[i], &v[i]);
	for (int i = 1; i <= n; i ++) scanf("%lld", &w[i]);

	for (int i = 1; i <= n; i ++) link(u[i], v[i]), link(v[i], u[i]);

	for (int i = 1; i <= n; i ++) {
		LL t = sumA[i] * sumA[i]  - sumB[i];
		sum = (sum + t) % MOD;
		maxx = max(maxx, m1[i] * m2[i]);
	}
	
	printf("%lld %lld\n", maxx, sum);

	return 0;
}
  • 用树来实现,遍历n个点,时间复杂度O(n),100分
#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 2e5 + 10, M = 2 * N, MOD = 10007;
int n;
int h[N], to[M], nxt[M], idx;
LL maxx, sum, w[N];

void add(int u, int v) { to[++ idx] = v; nxt[idx] = h[u]; h[u] = idx; }

void dfs(int r, int f, int g) {
	LL sumA = 0, sumB = 0, m1 = 0, m2 = 0;		//和,平方和,最大,次大 
	
	for (int i = h[r]; i; i = nxt[i]) {
		int v = to[i];
		if (v == f) continue;
		
		sumA = (sumA + w[v]) % MOD;
		sumB = (sumB + w[v] * w[v]) % MOD;
		if (w[v] > m1) m2 = m1, m1 = w[v];
		else if (w[v] > m2) m2 = w[v];
		
		dfs(v, r, f);
	}
	
	LL tmp = max(m1 * m2, w[r] * w[g]);
	maxx = max(maxx, tmp);
	
	tmp = (sumA * sumA - sumB + 2 * w[r] * w[g]) % MOD;
	sum = (sum + tmp) % MOD;	
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i < n; i ++) {
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v); add(v, u);
	}
	for (int i = 1; i <= n; i ++) scanf("%lld", &w[i]);
	
	dfs(1, 0, 0);

	printf("%lld %lld\n", maxx, sum);

	return 0;
}
2018年 提高组D1T3 赛道修建
  • 40分:退化成链,或者只修建一条赛道
#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 5e4 + 10, M = 2 * N;
int n, m;
int h[N], to[M], wt[M], nxt[M], idx;
struct edge{ 
	int a, b, l; 
}e[N];

void add(int u, int v, int w) { to[++ idx] = v; wt[idx] = w; nxt[idx] = h[u]; h[u] = idx; }

///
bool cmp1(edge x, edge y) {	return x.a < y.a; }
void f1() {
	sort(e+1, e+n, cmp1);
		
	int l = 0, r = 5e8, ans = 0;
	while (l <= r) {
		int mid = l + r >> 1;
		
		int cnt = 0, sum = 0;
		for (int i = 1; i < n; i ++) {
			sum += e[i].l;
			if (sum >= mid) {
				cnt ++;
				sum = 0;
			}
		}
		
		if (cnt >= m) ans = mid, l = mid + 1;
		else r = mid - 1;
	}
	printf("%d\n", ans);
}

///
int q[N], hh, tt, len[N];
void bfs(int x) {
	memset(len, -1, sizeof(len));
	hh = tt = 0;
	
	q[++ tt] = x;
	len[x] = 0;
	while (hh < tt) {
		int u = q[hh + 1];
		hh ++;
		for (int i = h[u]; i; i = nxt[i]) {
			int v = to[i];
			if (len[v] != -1) continue;
			q[++ tt] = v;
			len[v] = len[u] + wt[i];
		}
	}
}
void f2() {
	int maxx;
	
	//以任意点为根,距离最远的点就是直径上的端点之一 
	bfs(1);								
	maxx = 0;
	for (int i = 1; i <= n; i ++) {
		if (len[i] > len[maxx]) {
			maxx = i;
		} 
	}
	
	//以找到的端点为根,再找一次,距离最远的点就是直径上的另一个端点 
	bfs(maxx);
	maxx = 0;
	for (int i = 1; i <= n; i ++) {
		if (len[i] > len[maxx]) maxx = i;
	}
	
	printf("%d\n", len[maxx]);
}

///
int main() {
	scanf("%d%d", &n, &m);
	bool chain = true;
	for (int i = 1; i < n; i ++) {
		int a, b, l;
		scanf("%d%d%d", &a, &b, &l);
		
		e[i].a = a; e[i].b = b; e[i].l = l; 	//存边 
		add(a, b, l); add(b, a, l);				//邻接矩阵 
		
		if (b != a + 1) chain = false;
	}
	
	if (chain) f1();							//退化成链 
	else if (m == 1) f2();						//只修建一条赛道 

	return 0;
}
  • 正解100分
#include <bits/stdc++.h>
using namespace std;

const int N = 5e4 + 10, M = 2 * N;
int n, m, cnt;
int l, r, mid;
int h[N], to[M], wt[M], nxt[M], idx;
int f[N]; 

void add(int u, int v, int w) { to[++ idx] = v; wt[idx] = w; nxt[idx] = h[u]; h[u] = idx; }

//子树的根节点,父节点
void dfs(int u, int fa) {
	multiset <int> s;
	multiset <int> :: iterator it, it1;
	for (int i = h[u]; i; i = nxt[i]) {
		int v = to[i], w = wt[i];
		if (v == fa) continue;
		
		dfs(v, u);						//遍历根节点为v的子树,此时u是v的父节点
		if (f[v] + w >= mid)  cnt ++;	//赛道数量 + 1 
		else s.insert(f[v] + w);		//将长度不够的链保留下来 
	}
		
	while (!s.empty()) {
		it = s.begin();					//最短的一条链
		s.erase(it);
		it1 = s.lower_bound(mid - *it);	//试图找一条能和最短链组合成赛道且赛道最短的链 
		if (it1 == s.end()) {			
			f[u] = max(f[u], *it);		//如果找不到,将这条链的长度作为f[u]的候选项 
		}
		else {
			cnt ++;						//如果能找到,赛道数量 + 1
			s.erase(it1);
		}
	}
}

bool check() {
	memset(f, 0, sizeof(f));
	cnt = 0;
	dfs(1, 0);							//子树的根节点,父节点
	return cnt >= m;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; i ++) {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		add(u, v, w); add(v, u, w);
		r += w;
	}
	l = 0; r = r / m;					//长度最小的赛道的长度不超过总长度 / m 

	int ans = 0;
	while (l <= r) {
		mid = l + r >> 1;
		if (check()) ans = mid, l = mid + 1;
		else r = mid - 1;
	}
	printf("%d\n", ans);	
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值