题意:给一棵树,有点权也有边权,求最长点对价值,最长点对价值指的是这两点的路径的边权和加上这两个点的权值
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);
	}
}