P7846 「dWoi R2」Arcade hall / 街机厅 题解
JackMerryYoung · · 题解
前言
继 P7238「DCOI」迷失森林 后本人写的第二篇树形 DP 的题解。(终于把比赛写挂的题写完了...)
若有错误,请指出。
本题考察了选手的各种优化技巧。
我一开始没有想到
本人觉得书虫的题解有点奇怪深奥(太蒻了,看的时候差点没看懂),自己写了一篇。
本题较 P7238「DCOI」迷失森林 较为简单,不过看看我的提交记录就知道有多坑了...
(这件事告诉我们:特判与初始化很重要)
好了,废话不多说,又双叒叕开始讲题了!
正文
第一问
我们分类讨论。
设
1. t = 0
子节点可以选与父节点不一样的值,因此有
2. t = 1
子节点的值并没有要求,因此有
3. t = 2
子节点的值必须与父节点一样,因此有
于是将所有节点的值乘在一起,结果即为答案。
记得一开始乘个
第二问
依旧分类讨论。
设
1. t = 0
子节点必须与父节点不同,因此将每个子节点选与父节点不同的值时的最小值累加即可。
2. t = 1
子节点的值并没有要求,因此直接取个最小值。
3. t = 2
子节点的值必须与父节点一样,只能从
则答案为
小优化
如果你真这么写了,那么恭喜您拿到了部分分...
因为你枚举父子节点的点权要
正解:
既然没有后效性,那就在做完一个节点以后记录一下当节点
以及后面的最小:
于是就将第二问中,
这样就不必枚举子节点的点权了,从而将复杂度降至
代码
你们最想要的..
Talk is
// Problem: P7846 「dWoi R2」Arcade hall / 街机厅
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7846
// Memory Limit: 512 MB
// Time Limit: 1000 ms
#include <bits/stdc++.h>
#define MOD (unsigned long long)(1e9 + 7)
using namespace std;
struct Edge {
unsigned long long nxt, to, dis;
} edge[200005];
unsigned long long cnt, ans = 1;
unsigned long long head[200005], f[100005][105], g[100005];
unsigned long long pref[100005][105], suff[100005][105], all[100005];
bool visited[100005];
unsigned long long N, R;
void add(unsigned long long u, unsigned long long v, unsigned long long w)
{
++ cnt;
edge[cnt].to = v;
edge[cnt].nxt = head[u];
edge[cnt].dis = w;
head[u] = cnt;
}
void dfs1(unsigned long long u)
{
for(unsigned long long i = head[u]; i; i = edge[i].nxt)
{
unsigned long long v = edge[i].to, w = edge[i].dis;
if(visited[v]) continue;
visited[v] = true;
if(w == 0) g[v] = R - 1;
else if(w == 1) g[v] = R;
else if(w == 2) g[v] = 1;
else puts("114514");
ans = ((ans % MOD) * (g[v] % MOD)) % MOD;
dfs1(v);
}
}
void dfs2(unsigned long long u)
{
for(unsigned long long i = 1; i <= R; ++ i)
f[u][i] = i;
for(unsigned long long i = head[u]; i; i = edge[i].nxt)
{
unsigned long long v = edge[i].to, w = edge[i].dis;
if(visited[v]) continue;
visited[v] = true;
dfs2(v);
if(w == 0)
for(unsigned long long j = 1; j <= R; ++ j)
f[u][j] += min(pref[v][j - 1], suff[v][j + 1]);
if(w == 1)
for(unsigned long long j = 1; j <= R; ++ j)
f[u][j] += all[v];
if(w == 2)
for(unsigned long long j = 1; j <= R; ++ j)
f[u][j] += f[v][j];
}
pref[u][0] = LONG_LONG_MAX;
suff[u][R + 1] = LONG_LONG_MAX;
all[u] = LONG_LONG_MAX;
for(unsigned long long i = 1; i <= R; ++ i)
pref[u][i] = min(pref[u][i - 1], f[u][i]);
for(unsigned long long i = R; i >= 1; -- i)
suff[u][i] = min(suff[u][i + 1], f[u][i]);
for(unsigned long long i = 1; i <= R; ++ i)
all[u] = min(all[u], f[u][i]);
}
signed main()
{
srand(time(0));
cin >> N >> R;
bool flag = false;
for(unsigned long long i = 1; i < N; ++ i)
{
unsigned long long u, v, t;
cin >> u >> v >> t;
add(u, v, t);
add(v, u, t);
if(!t)
flag = true;
}
ans *= R;
unsigned long long Root = rand() % N;
visited[Root] = true;
dfs1(Root);
if(R == 1 && flag)
{
cout << "0 0";
return 0;
}
else
{
cout << ans << ' ';
}
memset(visited, 0, sizeof(visited));
visited[Root] = true;
dfs2(Root);
ans = LONG_LONG_MAX;
for(unsigned long long i = 1; i <= R; ++ i)
ans = min(ans, f[Root][i]);
cout << ans << endl;
return 0;
}
后言
这里感谢毒瘤出题人!
本题 DP 难度不大,不过需要加点优化,希望每一位读者都能够学好树形 DP(以后把我吊打)。