A、点对最大值
题意:给一棵树,有点权也有边权,求最长点对价值,最长点对价值指的是这两点的路径的边权和加上这两个点的权值
dp[v] 表示一个端点为 v,另一个节点是在以 v 为根的子树中 的最大点对价值,考虑 u 是 v 的父节点,转移到 dp[u] 时,我们只需要考虑,以 v 为另一个端点和以 其他点为另一个端点并且加上 dp[u](即除了这个子链的其他链的最大值),然后在遍历玩了这个点为根节点的子树之后,将 dp[u] 加上 u 的点权,这时,dp[u] 就表示一个端点为 u ,另一个节点是在以 u 为根的子树中 的最大点对价值,即完成了状态转移。
#include <bits/stdc++.h> #define ll long long #define sc scanf #define pr printf using namespace std; const int MAXN = 1e6 + 5; struct edge { int to; ll w; int nex; }e[MAXN * 2]; int head[MAXN], tot; ll dp[MAXN], val[MAXN]; ll ans = 0; void init(int n) { for (int i = 1; i <= n; i++) { head[i] = -1; dp[i] = -1e18 - 7; } tot = 1; ans = -1e18 - 7; } void add(int a, int b, ll w) { e[tot] = edge{ b,w,head[a] }; head[a] = tot++; } void dfs(int u, int fa) { for (int i = head[u]; i + 1; i = e[i].nex) { int v = e[i].to; ll w = e[i].w; if (v == fa) continue; dfs(v, u); ans = max(ans, dp[u] + dp[v] + w - val[v]); ans = max(ans, dp[u] + w + val[v]); dp[u] = max(dp[u], dp[v] + w - val[v]); dp[u] = max(dp[u], w + val[v]); } dp[u] += val[u]; ans = max(ans, dp[u]); } int main() { int T; sc("%d", &T); while (T--) { int n; sc("%d", &n); init(n); for (int i = 1; i < n; i++) { int to; ll w; sc("%d%lld", &to, &w); add(i + 1, to, w); add(to, i + 1, w); } for (int i = 1; i <= n; i++) sc("%lld", &val[i]); dfs(1, 0); pr("%lld\n", ans); } }
B、减成一
题意:n个数,每次选择一个区间是的区间内所有数字减一,求将所有数变成 1 的最小次数。
和上周中北大学的一个题类似,将这个序列看成若干个不下降的序列,然后序列内通过若干次区间减一,变成这些区间的最小值,并且由于前一个区间的最大值肯定小于下一个区间的最小值,所以不需要额外的代价,可以将所有数字变成第一个区间的最小值,即第一个数字,所以我们只需要在进行 a[1] - 1 次,即可将所有数字变成 1
#include <bits/stdc++.h> #define ll long long #define sc scanf #define pr printf using namespace std; const int MAXN = 2e5 + 5; ll a[MAXN]; int main() { int T; sc("%d", &T); while (T--) { int n; sc("%d", &n); ll ans = 0; a[0] = 1; for (int i = 1; i <= n; i++) { sc("%lld", &a[i]); if (a[i - 1] < a[i]) ans += a[i] - a[i - 1]; } pr("%lld\n", ans); } }
C、面积
题意:输出面积
x * x + (x / 2) ^ 2 * PI * 2
#include <bits/stdc++.h> #define ll long long #define sc scanf #define pr printf using namespace std; int main() { int T; sc("%d", &T); while (T--) { ll n; sc("%lld", &n); double ans = n * n + 2 * (n / 2.0) * (n / 2.0) * 3.14; pr("%.2lf\n", ans); } }
D、扔硬币
题意:有n个硬币,每个硬币正面反面概率都是50%,投出n个硬币,求至少有m个硬币是反面的前提下,有k个硬币是正面的概率
显然是一个概率论问题,就用 有k个硬币是正面的概率 / 至少有m个硬币是反面 即可,并且由于数据库较小,每次暴力求前缀和也能过
注意特判 0 的情况
#include <bits/stdc++.h> #define ll long long #define sc scanf #define pr printf using namespace std; const int MAXN = 2e5 + 5; const ll mod = 1e9 + 7; ll invi[MAXN], fac[MAXN], inv[MAXN]; void init() { invi[0] = invi[1] = 1; fac[0] = 1; inv[0] = 1; for (int i = 2; i < MAXN; i++) invi[i] = invi[mod % i] * (ll)(mod - mod / i) % mod; for (int i = 1; i < MAXN; i++) { fac[i] = fac[i - 1] * i % mod; inv[i] = invi[i] * inv[i - 1] % mod; } } ll C(ll n, ll m) { ll ans = fac[n] * inv[m] % mod * inv[n - m] % mod; return ans; } ll power(ll a, ll b) { ll res = 1; while (b) { if (b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } ll a[MAXN]; int main() { init(); int T; sc("%d", &T); while (T--) { ll n, m, k; sc("%lld%lld%lld", &n, &m, &k); if (m + k > n) pr("0\n"); else { ll ans1 = C(n, k) % mod; ll ans2 = 0; for (int i = 0; i <= n - m; i++) ans2 = (ans2 + C(n, i)) % mod; pr("%lld\n", ans1 * power(ans2, mod - 2) % mod); } } }
E、赛马
题意:每个人有n个马,每个马一个战力值,求第一个人最多能赢多少场。
先将第一个人的马的战力值存在multiset中,然后对于每一个别人的马,找一匹比这个马战斗力大的战斗力最小的马,然后删掉即可,如果没有比他大的就选一匹战斗力最小的马和它打
#include <bits/stdc++.h> #define ll long long #define sc scanf #define pr printf using namespace std; const int MAXN = 2e5 + 5; int a[MAXN]; int main() { int T; sc("%d", &T); while (T--) { multiset<int>s; int n; sc("%d", &n); for (int i = 1; i <= n; i++) { sc("%d", &a[i]); s.insert(a[i]); } int ans = 0; for (int i = 1; i <= n; i++) { int t; sc("%d", &t); auto it = s.upper_bound(t); if (it == s.end()) continue; else { ans++; s.erase(it); } } pr("%d\n", ans); } }
F、三角形
题意:将长度为 a 的木棒分成若干段,要求不能组成三角形,最多能被分成几段
显然按照斐波那契数列来分是最优的,如果剩下的值不足以构成下一个斐波那契数,那么我们将剩下的数全部放在上一个数。
由于题目给的范围比较大,直接上JAVA,真香
import java.math.BigInteger; import java.util.*; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int T = sc.nextInt(); BigInteger zero = new BigInteger("0"); BigInteger[] fib = new BigInteger[100]; fib[1] = new BigInteger("1"); fib[2] = new BigInteger("1"); for (int i=3;i<100;i++) fib[i] = fib[i-1].add(fib[i-2]); for (int i=2;i<100;i++){ fib[i]=fib[i].add(fib[i-1]); } for (int i=0;i<T;i++){ BigInteger n = sc.nextBigInteger(); for (int j=1;j<100;j++) { if (n.subtract(fib[j]).compareTo(zero) < 0) { System.out.println(j-1); break; } } } } }
H、直线
题意:一个平面,n 条直线,最多有多少个交点
在纸上模拟,很容易得到答案是 1 到 n 的等差数列,由于数据较大,直接上JAVA,真香
import java.math.BigInteger; import java.util.*; public class Main{ public static void main(String[] args){ BigInteger one = new BigInteger("1"); BigInteger two = new BigInteger("2"); Scanner sc = new Scanner(System.in); int T = sc.nextInt(); for (int i=0;i<T;i++){ BigInteger n = sc.nextBigInteger(); n = n.multiply(n.subtract(one)).divide(two); System.out.println(n); } } }
J、最大值
题意:对于字符串中一个非前缀子串 它与字符串的前缀相等的最大长度
这个定义就是KMP的next数组的定义,所以直接跑一遍next数组即可
(字符串哈希+二分应该也可以通过此题)
#include <bits/stdc++.h> #define ll long long #define sc scanf #define pr printf using namespace std; const int MAXN = 2e5 + 5; char s[MAXN]; int len, nex[MAXN], n, m; void getnex() { memset(nex, 0, sizeof(nex)); int i = 0, j = -1; nex[0] = -1; while (i < len) { if (j == -1 || s[i] == s[j]) { i++, j++; nex[i] = j; } else j = nex[j]; } } int main() { int T; sc("%d", &T); while (T--) { sc("%s", s); len = strlen(s); getnex(); int ans = 0; for (int i = 1; i <= len; i++) { ans = max(ans, nex[i]); } pr("%d\n", ans); } }