牛客周赛 Round 88


A. 吔我大炮!

点击查看代码
void solve() {
    int a, b, c;
    std::cin >> a >> b >> c;
    if (a * b <= c) {
    	std::cout << "YES\n";
    } else {
    	std::cout << "NO\n";
    }
}

B. 梦间

题意:判断三角形有没有一条中线是坐标轴。

枚举两个点求出中点然后和另一个点判断是不是在坐标轴上。

点击查看代码
void solve() {
   std::pair<int, int> a[3];
   for (int i = 0; i < 3; ++ i) {
   		std::cin >> a[i].first >> a[i].second;
   } 

   for (int i = 0; i < 3; ++ i) {
   		for (int j = i + 1; j < 3; ++ j) {
   			int k = 0;
   			while (k == i || k == j) {
   				++ k;
   			}

   			int x = (a[i].first + a[j].first) * 10 / 2, y = (a[i].second + a[j].second) * 10 / 2;
   			if ((x == 0 && a[k].first == 0) || (y == 0 && a[k].second == 0)) {
   				std::cout << "Yes\n";
   				return;
   			}
   		}
   }

   std::cout << "No\n";
}

C. 坠入

题意:和\(B\)差不多,不管是问有没有和坐标轴平行。

判断中点和另一个点的线段是不是和坐标轴平行。

点击查看代码
void solve() {
   std::pair<int, int> a[3];
   for (int i = 0; i < 3; ++ i) {
   		std::cin >> a[i].first >> a[i].second;
   } 

   for (int i = 0; i < 3; ++ i) {
   		for (int j = i + 1; j < 3; ++ j) {
   			int k = 0;
   			while (k == i || k == j) {
   				++ k;
   			}

   			int x = (a[i].first + a[j].first) * 10 / 2, y = (a[i].second + a[j].second) * 10 / 2;
   			if ((x == a[k].first * 10) || (y == a[k].second * 10)) {
   				std::cout << "Yes\n";
   				return;
   			}
   		}
   }

   std::cout << "No\n";
}

D. 漫步

题意:给你一个\(x\),求一个\(z, 1\leq z < x\),使得\(x | (x + z) = x + z\)\(|\)是或运算。

找到不高于\(x\)最高位1的\(x\)是零的那一位。

点击查看代码
void solve() {
    i64 x;
    std::cin >> x;
    bool flag = false;
    for (int i = 60; i >= 0; -- i) {
    	if (x >> i & 1) {
    		flag = true;
    	} else if (flag) {
    		std::cout << (1ll << i) << "\n";
    		return;
    	}
    }

    std::cout << -1 << "\n";
}

E. 秘藏

题意:一个\(a \times n\)的数组,你从\((1, 1)\)开始,要到\((1, n)\)或者\((2, n)\)。每个格子有价值。每次从当前行到下一列不花费价值,跳到另一行花费\(k\)的价值。求最大价值。

\(f[0/1][i]\)为到第0/1行\(i\)列的最大价值,那么\(f[j][i + 1] = \max(f[j][i + 1], f[j][i] + a[j][i + 1])\),如果\(f[j][i] \geq k\)那么,\(f[j \oplus1][i + 1] = \max(f[j \oplus 1][i + 1], f[j][i] + a[j \oplus 1][i + 1] - k)\)

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector a(2, std::vector<i64>(n));
    for (int i = 0; i < 2; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		std::cin >> a[i][j];
    	}
    }

    const i64 inf = 1e18;
    std::vector f(2, std::vector<i64>(n, -inf));
    f[0][0] = a[0][0];
    for (int i = 0; i + 1 < n; ++ i) {
    	for (int j = 0; j < 2; ++ j) {
    		f[j][i + 1] = std::max(f[j][i + 1], f[j][i] + a[j][i + 1]);
    		if (f[j][i] >= k) {
    			f[j ^ 1][i + 1] = std::max(f[j ^ 1][i + 1], f[j][i] + a[j ^ 1][i + 1] - k);
    		}
    	}
    }

    std::cout << std::max(f[0][n - 1], f[1][n - 1]) << "\n";
}

F. 回响

题意:一个数组,有些地方已经有数了,有些没数,你要把他们填上。使得相邻两个数正好相差\(1\)

考虑那些最靠近的两个非零数,我们只需要在他们中间填。如果\(|a_i - a_j| = j - i\),那么直接看大小关系顺序填就行。
否则如果\(a_i \leq a_j\),我们就\(a_i + 1, a_i + 2..\)这样填,一直到\(a_k - a_j = j - k\)就可以递减填。\(a_i > a_j\)同样的考虑方式。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    bool flag = false;
    for (int i = 0; i < n; ++ i) {
    	if (a[i] != 0) {
    		if (!flag) {
    			for (int k = i - 1; k >= 0; -- k) {
    				a[k] = a[k + 1] + 1;
    			}

    			flag = true;
    		}
    		int j = i + 1;
    		while (j < n && a[j] == 0) {
    			++ j;
    		}

    		if (j == n - 1) {
    			for (int k = j + 1; k < n; ++ k) {
    				a[k] = a[k - 1] + 1;
    			}
    		} 

    		if (a[i] < a[j]) {
    			if (j - i == a[j] - a[i]) {
    				for (int k = i + 1; k < j; ++ k) {
    					a[k] = a[k - 1] + 1;
    				}
    			} else {
    				for (int k = i + 1; k < j; ++ k) {
    					a[k] = a[k - 1] + 1;
    					if (a[k] - a[j] == j - k) {
    						for (int l = k + 1; l < j; ++ l) {
    							a[l] = a[l - 1] - 1;
    						}
    						break;
    					}
    				}
    			} 
    		} else {
    			if (j - i == a[i] - a[j]) {
    				for (int k = i + 1; k < j; ++ k) {
    					a[k] = a[k - 1] - 1;
    				}
    			} else {
    				for (int k = i + 1; k < j; ++ k) {
    					a[k] = a[k - 1] - 1;
    					if (a[j] - a[k] == j - k) {
    						for (int l = k + 1; l < j; ++ l) {
    							a[l] = a[l - 1] + 1;
    						}
    						break;
    					}
    				}
    			} 
    		}

    		i = j - 1;
    	}
    }

    for (int i = 1; i < n; ++ i) {
    	if (std::abs(a[i] - a[i - 1]) != 1) {
    		std::cout << -1 << "\n";
    		return;
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << a[i] << " \n"[i == n - 1];
    }
}

G. 升!龙!

题意:给你一棵树,一棵树的价值为每个叶子节点到根的路径和的最大值。现在每次把一个子树移动到一个节点下面,求价值。

我们可以把每个节点子树的叶子节点的值存下来,因为非叶子节点乘叶子节点的数量不会很大。
那么\(dfs\)求出每个点从上往下的路径最大值和从下往上的路径最大值。并用一个\(multiset\)存每个叶子的值,那么把\(x\)接到\(y\)我们可以把\(x\)这棵子树的叶子的值都删掉,然后把\(x\)父亲到根的和加进来,再把子树的从下往上的最大值加上\(y\)从从上往下的值的和加加进来,此时set里的最大值就是答案。最后还原之前的set就行。

点击查看代码
void solve() {
    int n, q;
    std::cin >> n >> q;
    std::vector<i64> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<std::vector<int>> adj(n);
    std::vector<i64> sum(n), sub(n), fa(n);
    for (int i = 1; i < n; ++ i) {
    	std::cin >> fa[i];
    	-- fa[i];
    	adj[fa[i]].push_back(i);
    }

    std::vector<std::vector<i64>> b(n);
    std::multiset<i64> s;
    auto dfs = [&](auto & self, int u) -> void {
    	for (auto & v : adj[u]) {
    		sum[v] = a[v] + sum[u];
    		self(self, v);
    		sub[u] = std::max(sub[u], sub[v] + a[u]);
    		for (auto & x : b[v]) {
    			b[u].push_back(x);
    		}
    	}

    	if (adj[u].empty()) {
    		sub[u] = a[u];
    		s.insert(sum[u]);
    		b[u].push_back(sum[u]);
    	}
    };

    fa[0] = -1;
    sum[0] = a[0];
    dfs(dfs, 0);

    while (q -- ) {
    	int x, y;
    	std::cin >> x >> y;
    	-- x, -- y;
    	for (auto & v : b[x]) {
    		s.extract(v);
    	}

    	s.insert(sum[fa[x]]);
    	s.insert(sub[x] + sum[y]);
    	std::cout << *s.rbegin() << "\n";
    	s.extract(sub[x] + sum[y]);
    	s.extract(sum[fa[x]]);
		
		for (auto & v : b[x]) {
    		s.insert(v);
    	}
    }	
}
posted @ 2025-04-06 21:05  maburb  阅读(98)  评论(0)    收藏  举报