1104上午考试总结
1104上午考试
T1
题目大意:
如果一个数字的十进制表示中,有大于等于 1个 1,或者有大于等于 2 个 2,或者有大于等于 3 个 3,或者有大于等于 4个 4,或者有大于等于 5 个 5,或者有大于等于 6 个 6,或者有大于等于 7个 7,或者有大于等于 8 个 8,或者有大于等于 9 个 9,这个数字是 good number。
问 L 到 R 之间 (包含 L 和 R) 有多少个 good number? (\(L, R <= 1e9\))
记忆化搜索.(数位DP也可, 但我不会)
单步容斥一下,可以求出不是good number的数字.
我们从高位向低位填数, \(f[now][s1][s2][s3][s4][s5][s6][s7][s8][s9]\)表示改填第\(now\)位时,数字\(i\)已经填了\(si\)个时的不是good number的数的个数.然后搜他就完了.注意那个\(lim\)表示限制,如果说\(now + 1\)位上填的数是\(a[now + 1]\),那么\(now\)位最高就只能填到\(a[now]\),不可填到9.
#include <bits/stdc++.h>
using namespace std;
int L, R, cnt, ans, a[11], t[11], f[11][1][2][3][4][5][6][7][8][9];
int dfs(int now, int lim, int s1, int s2, int s3, int s4, int s5, int s6, int s7, int s8, int s9) {
if(s1 >= 1 || s2 >= 2 || s3 >= 3 || s4 >= 4 || s5 >= 5 || s6 >= 6 || s7 >= 7 || s8 >= 8 || s9 >= 9) return 0;
if(!now) return 1;
if(!lim && f[now][s1][s2][s3][s4][s5][s6][s7][s8][s9] != -1) return f[now][s1][s2][s3][s4][s5][s6][s7][s8][s9];
int up = lim ? a[now] : 9, res = 0;
for(int i = 0;i <= up; i++) {
if(i == 0) res += dfs(now - 1, lim && i == up, s1, s2, s3, s4, s5, s6, s7, s8, s9);
if(i == 1) res += dfs(now - 1, lim && i == up, s1 + 1, s2, s3, s4, s5, s6, s7, s8, s9);
if(i == 2) res += dfs(now - 1, lim && i == up, s1, s2 + 1, s3, s4, s5, s6, s7, s8, s9);
if(i == 3) res += dfs(now - 1, lim && i == up, s1, s2, s3 + 1, s4, s5, s6, s7, s8, s9);
if(i == 4) res += dfs(now - 1, lim && i == up, s1, s2, s3, s4 + 1, s5, s6, s7, s8, s9);
if(i == 5) res += dfs(now - 1, lim && i == up, s1, s2, s3, s4, s5 + 1, s6, s7, s8, s9);
if(i == 6) res += dfs(now - 1, lim && i == up, s1, s2, s3, s4, s5, s6 + 1, s7, s8, s9);
if(i == 7) res += dfs(now - 1, lim && i == up, s1, s2, s3, s4, s5, s6, s7 + 1, s8, s9);
if(i == 8) res += dfs(now - 1, lim && i == up, s1, s2, s3, s4, s5, s6, s7, s8 + 1, s9);
if(i == 9) res += dfs(now - 1, lim && i == up, s1, s2, s3, s4, s5, s6, s7, s8, s9 + 1);
}
return f[now][s1][s2][s3][s4][s5][s6][s7][s8][s9] = res;
}
int calc(int x) {
cnt = 0;
while(x) a[++ cnt] = x % 10, x /= 10;
return dfs(cnt, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
int main() {
cin >> L >> R; memset(f, -1, sizeof(f));
printf("%d", R - L + 1 - calc(R) - calc(L - 1));
return 0;
}
T2
题目大意:
小M总共有N个花园,它们构成了一棵1号节点为根的有根树,所有边权均为1。定义一个点x的val[x]=min{y|y属于x的子树}。从第一时刻到第N时刻,每一时刻小M会从1号节点出发,按照一定的规则拜访一个花园(这个花园是确定的):
1.如果小M在i号节点,它是叶子节点或者所有儿子都被拜访过则停在该节点。
2.如果第i号节点存在儿子没有拜访过,则找到所有未拜访的儿子中val最小的儿子继续行走。
3.小M会拜访最终停在的花园,且每次拜访后会返回一号节点。因为路程的原因小M的心情点数会减去1号节点到拜访节点的距离。
因为走路会很累,所以小M可以在任意时刻停止拜访花园在花园中共有M阵和风吹过,每阵风会遍历x[i],y[i]这条链上的所有花园,且有z[i]的声音舒适度。最终小M的心情点数会加上max(size[i]-1,0)*z[i],size[i]表示小M最终拜访过的花园在这条链上的个数。请问在什么时候停止拜访,小M的心情值最高呢。
首先遍历顺序是一定的.对于每个节点\(x\),它的贡献是\(-dep[x] + \sum z\).
但是题目说了"心情点数会加上\(max(size[i]-1,0)*z[i]\)",这个减一就有点难搞,我们可以找到遍历顺序里第一个经过这条链的点,然后这个点的贡献不加当前的\(z\).对于给点加\(z\),直接树上点的差分就好了,记得在求lca的时候找出那个遍历顺序里第一个经过这条链的点是\(x\)还是\(y\).然后按遍历顺序统计答案就好了.
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5, inf = 1e9;
vector <int> v[N];
int n, m, cnt, ans, now, P, a, b;
int w[N], f[N][21], dfn[N], val[N], dep[N], head[N];
struct edge { int nxt, to; } e[N << 1];
void add(int x, int y) {
e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
}
void get_val(int x, int fa) {
val[x] = x; dep[x] = dep[fa] + 1; f[x][0] = fa;
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == fa) continue;
get_val(y, x); val[x] = min(val[x], val[y]);
}
}
void make_f() {
for(int i = 1;i <= 20; i++)
for(int j = 1;j <= n; j++) f[j][i] = f[f[j][i - 1]][i - 1];
}
int LCA(int x, int y) {
int tag = 0;
if(dep[x] < dep[y]) tag = 1, swap(x, y);
for(int i = 20;i >= 0; i--) if(dep[x] - dep[y] >= (1 << i)) x = f[x][i];
if(x == y) return x;
for(int i = 20;i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
a = val[x]; b = val[y]; if(tag) swap(a, b);
return f[x][0];
}
void make_w(int x, int fa) {
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == fa) continue;
make_w(y, x); w[x] += w[y];
}
}
void get_dfn(int x, int fa) {
priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == fa) continue;
q.push(make_pair(val[y], y));
}
while(!q.empty()) {
int y = q.top().second; q.pop();
get_dfn(y, x);
}
dfn[++ cnt] = x;
}
signed main() {
n = read(); m = read(); ans = -inf;
for(int i = 1, x, y;i < n; i++)
x = read(), y = read(), add(x, y), add(y, x);
get_val(1, 0); make_f(); cnt = 0;
for(int i = 1, x, y, z;i <= m; i++) {
x = read(); y = read(); z = read();
int lca = LCA(x, y);
if(x == lca) { w[f[y][0]] += z; w[f[x][0]] -= z; }
else if(y == lca) { w[f[x][0]] += z; w[f[y][0]] -= z; }
else {
if(a < b) { w[f[x][0]] += z; w[y] += z; w[lca] -= z; w[f[lca][0]] -= z; }
else { w[f[y][0]] += z; w[x] += z; w[lca] -= z; w[f[lca][0]] -= z; }
}
}
get_dfn(1, 0); make_w(1, 0);
for(int i = 1;i <= n; i++) w[i] -= dep[i] - 1;
for(int i = 1;i <= cnt; i++) now += w[dfn[i]], ans = max(ans, now);
printf("%lld", ans);
return 0;
}