树形DP

1072. 树的最长路径

题意:求带权树中的一条最长路径。

  所求一定是所有点向下最长的两条边之和中的最大值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 1e4 + 5, M = 2 * N;

int n, tot, ans;
int ver[M], edge[M], nxt[M], head[N];

void add(int x, int y, int z) {
    ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
}

int dfs(int x, int fa) {
    int maxd = 0; // 从当前点向下最大长度
    int d1 = 0, d2 = 0;
    for (int i = head[x]; i; i = nxt[i]) {
        int y = ver[i];
        if (y == fa) continue;
        int dis = dfs(y, x) + edge[i];
        maxd = max(maxd, dis);
        if (dis >= d1) d2 = d1, d1 = dis;
        else if (dis > d2) d2 = dis; 
    }
    ans = max(ans, d1 + d2);
    return maxd;
}

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i < n; ++i) {
        int x, y, z;
        cin >> x >> y >> z;
        add(x, y, z); add(y, x, z);
    }
    dfs(1, -1);
    cout << ans << '\n';
    return 0;
}

1073. 树的中心

题意:一棵包含 $n$ 个结点和 $n-1$ 条无向边,每条边都有一个权值,在树中找到一点,使得该点到树中其他结点的最远距离最近,输出所求点到树中其他结点的最远距离。

  求出每个点到其他点的最长距离,输出最短的一个,做两次 dfs,一次向下,一次向上,找出最长向下距离和最长向上距离,向上时可能需要次长向下距离。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 1e4 + 5, M = N << 1;

int n, tot;
int head[N], ver[M], edge[M], nxt[M];
int d1[N], d2[N], up[N], p[N];

void add(int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
}

int dfs1(int x, int fa) {
	d1[x] = d2[x] = -INF;
	for (int i = head[x]; i; i = nxt[i]) {
		int y = ver[i];
		if (y == fa) continue;
		int d = dfs1(y, x) + edge[i];
		if (d >= d1[x]) d2[x] = d1[x], d1[x] = d, p[x] = y;
		else if (d > d2[x]) d2[x] = d;
	}
	if (d1[x] == -INF) d1[x] = d2[x] = 0;
	return d1[x];
}

void dfs2(int x, int fa) {
	for (int i = head[x]; i; i = nxt[i]) {
		int y = ver[i];
		if (y == fa) continue;
		if (p[x] == y) up[y] = max(up[x], d2[x]) + edge[i];
		else up[y] = max(up[x], d1[x]) + edge[i];
		dfs2(y, x);
	}
}

int main() 
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	int x, y, z;
	for (int i = 1; i < n; ++i) {
		cin >> x >> y >> z;
		add(x, y, z); add(y, x, z);
	}
	dfs1(1, -1);
	dfs2(1, -1);
	int ans = d1[1];
	for (int i = 2; i <= n; ++i) {
		ans = min(ans, max(d1[i], up[i]));
	}
	cout << ans << '\n';
    return 0;
}

1075. 数字转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 5e4 + 5;

int n, tot, ans;
int ver[N], nxt[N], head[N];
int sum[N], st[N];

void add(int x, int y) {
	ver[++tot] = y, nxt[tot] = head[x], head[x] = tot;
}

int dfs(int x) {
	int d1 = 0, d2 = 0;
	for (int i = head[x]; i; i = nxt[i]) {
		int y = ver[i];
		int d = dfs(y) + 1;
		if (d >= d1) d2 = d1, d1 = d;
		else if (d > d2) d2 = d;
	}
	ans = max(ans, d1 + d2);
	return d1;
}

int main() 
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		for (int j = i * 2; j <= n; j += i) {
			sum[j] += i;
		}
	}
	for (int i = 2; i <= n; ++i) {
		if (i > sum[i]) {
			add(sum[i], i);
			st[i] = 1;
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (!st[i]) dfs(i);
	}
	cout << ans << '\n';
    return 0;
}

数位DP

1081. 度的数量

题意:求给定区间 $[X,Y]$ 中满足下列条件的整数个数:这个数恰好等于 $K$ 个互不相等的 $B$ 的整数次幂之和。

  所求转化成求$dp(y)-dp(x-1)$的形式,$dp(x)$表示区间 $[0,x]$ 的个数,条件即 B 进制 K 个 1 的情况。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 35;

int x, y, k, b;
int C[N][N];

int dp(int x) {
    if (!x) return 0;
    vector<int> num;
    while (x) num.push_back(x % b), x /= b;
    int res = 0, last = 0;
    for (int i = num.size() - 1; i >= 0; --i) {
        int t = num[i];
        if (t) {
            res += C[i][k-last];
            if (t > 1) {
                if (k >= last + 1) res += C[i][k-last-1];
                break;
            }
            else {
                last++;
                if (last > k) break;
            }
        }
        if (!i && last == k) res++;
    }
    return res;
}

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> x >> y >> k >> b;
    for (int i = 0; i <= N; ++i) {
        C[i][0] = 1;
        for (int j = 1; j <= i; ++j) C[i][j] = C[i-1][j] + C[i-1][j-1];
    }
    cout << dp(y) - dp(x - 1) << '\n';
    return 0;
}

1082. 数字游戏

题意:求区间 $[a,b]$ 内的不降数个数。

  用 $f[i][j]$ 表示 $i$ 位数且最高位为 $j$ 的不降数的方案数,则 $f[i][j]=\sum_{k=j}^9f[i-1][k]$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 11;

int a, b;
int f[N][10];

void init() {
    for (int i = 0; i <= 9; ++i) f[1][i]= 1;
    for (int i = 2; i < N; ++i) {
        for (int j = 0; j <= 9; ++j) {
            for (int k = j; k <= 9; ++k) {
                f[i][j] += f[i-1][k];
            }
        }
    }
}

int dp(int x) {
    if (!x) return 1;
    vector<int> num;
    while (x) num.push_back(x % 10), x /= 10;
    int res = 0, last = 0;
    for (int i = num.size() - 1; i >= 0; --i) {
        int t = num[i];
        for (int j = last; j < t; ++j) {
            res += f[i+1][j];
        }
        if (t < last) break;
        last = t;
        if (!i) res++; // 该数合法
    }
    return res;
}

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    init();
    while (cin >> a >> b) {
        cout << dp(b) - dp(a-1) << '\n';
    }
    return 0;
}

1083. Windy数

题意:求区间 $[a,b]$ 内的 Windy 数,Windy 数指:不含前导 0 且相邻两个数字之差至少为 2 的正整数。

  用 $f[i][j]$ 表示 $i$ 位数且最高位为 $j$ 的 Windy 数个数,则 $f[i][j]=\sum_{|j-k|\ge2}f[i-1][k]$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 11;

int a, b;
int f[N][10];

void init() {
    for (int i = 0; i <= 9; ++i) f[1][i] = 1;
    for (int i = 2; i < N; ++i) {
        for (int j = 0; j <= 9; ++j) {
            for (int k = 0; k <= 9; ++k) {
                if (abs(j - k) >= 2) f[i][j] += f[i-1][k];	
             }
        }
    }
}

int dp(int x) {
    if (!x) return 0;
    vector<int> num;
    while (x) num.push_back(x % 10), x /= 10;
    int res = 0, last = -2;
    for (int i = num.size() - 1; i >= 0; --i) {
        int t = num[i];
        for (int j = (i == (int)num.size() - 1); j < t; ++j) {
            if (abs(j - last) >= 2) res += f[i+1][j];
        }
        if (abs(t - last) < 2) break;
        last = t;
        if (!i) res++;
    }
    // 有前导 0 的情况
    for (int i = 1; i < (int)num.size(); ++i) {
        for (int j = 1; j <= 9; ++j) {
            res += f[i][j];
        }
    }
    return res;
}

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    init();
    cin >> a >> b;
    cout << dp(b) - dp(a - 1) << '\n';
    return 0;
}

记忆化搜索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 11;

int l, r, len;
int dp[N][10], a[11];

int dfs(int cur, int last, int st, int lim) { // st 是否含前导 0
    if (!cur) return 1;
    if (!lim && ~dp[cur][last]) return dp[cur][last];
    int res = 0, up = lim ? a[cur] : 9;
    for (int i = 0; i <= up; ++i) {
        if (abs(i - last) < 2) continue;
        if(st && !i) res += dfs(cur - 1, -2, 1, lim && i == up);
        else res += dfs(cur - 1, i, 0, lim && i == up);
    }
    if (!lim && !st) dp[cur][last] = res;
    return res;
}

int part(int x) {
    len = 0;
    while (x) a[++len] = x % 10, x /= 10;
    memset(dp, -1, sizeof dp);
    return dfs(len, -2, 1, 1);
}

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> l >> r;
    cout << part(r) - part(l - 1) << '\n';
    return 0;
}

1084. 数字游戏 II

题意:求区间 $[a,b]$ 内的模数个数,模数指:各位数字之和 $\mod N$ 为 $0$。

  用 $f[i][j][k]$ 表示 $i$ 位数且最高位为 $j$ 所有数字和 $\mod N$ 为 $k$ 的数的个数,则 $f[i][j][k]=\sum_{x=0}^9f[i-1][x][((k-j)%N+N)%N]$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 11, M = 105;

int a, b, p;
int f[N][10][M];

int mod(int x, int y) {
    return ((x % y) + y) % y;
}

void init() {
    memset(f, 0, sizeof(f));
    for (int i = 0; i <= 9; ++i) f[1][i][i%p] = 1;
    for (int i = 2; i < N; ++i) {
        for (int j = 0; j <= 9; ++j) {
            for (int k = 0; k < p; ++k) {
                for (int x = 0; x <= 9; ++x) {
                    f[i][j][k] += f[i-1][x][mod(k-j,p)];
                }
            }
        }
    }
}

int dp(int x) {
    if (!x) return 1;
    vector<int> num;
    while (x) num.push_back(x % 10), x /= 10;
    int res = 0, last = 0;
    for (int i = num.size() - 1; i >= 0; --i) {
        int t = num[i];
        for (int j = 0; j < t; ++j) {
            res += f[i+1][j][mod(-last, p)];
        }
        last += t;
        if (!i && last % p == 0) res++;
    }
    return res;
}

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin >> a >> b >> p) {
        init();
        cout << dp(b) - dp(a - 1) << '\n';
    }
    return 0;
}

1085. 不要62

题意:求区间 $[a,b]$ 内不含 4 和 62 的数的个数。

  用 $f[i][j]$ 表示 $i$ 位数且最高位为 $j$ 满足条件的数的个数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 11;

int n, m;
int f[N][10];

void init() {
    for (int i = 0; i <= 9; ++i) f[1][i] = 1;
    f[1][4] = 0;
    for (int i = 2; i < N; ++i) {
        for (int j = 0; j <= 9; ++j) {
            if (j == 4) continue;
            for (int k = 0; k <= 9; ++k) {
                if (k == 4 || (j == 6 && k == 2)) continue;
                f[i][j] += f[i-1][k];
            }
        }
    }
}

int dp(int x) {
    if (!x) return 1;
    vector<int> num;
    while (x) num.push_back(x % 10), x /= 10;
    int res = 0, last = 0;
    for (int i = num.size() - 1; i >= 0; --i) {
        int t = num[i];
        for (int j = 0; j < t; ++j) {
            if (j == 4 || (last == 6 && j == 2)) continue;
            res += f[i+1][j];
        }
        if (t == 4 || (last == 6 && t == 2)) break;
        last = t;
        if (!i) res++;
    }
    return res;
}

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    init();
    while (cin >> n >> m && (n + m)) {
        cout << dp(m) - dp(n - 1) << '\n';
    }
    return 0;
}