P10693 题解
I_am_kunzi · · 题解
P10693 题解
温馨提醒:本题解约一千字,请耐心看完。
题目思路
首先所有题解都会告诉你要连一条从
先看样例,这对应着一种情况:如果所有的
于是我搓了另一组数据:
5
2 3 4 5 5
我将刚刚的结论带入,发现这与真正的答案并不相同。用刚刚的结论得到的结果为
这时我们就可以引出开头说的连边方式的原因了:我们假设让
连边方式有了,答案怎么确定呢?这时研究一下我给出的数据就会发现:当
然而我们模拟样例连边就会发现,图中形成了两个部分:一个是
但是还有最后的问题:上一段中的最大路径应该怎么求?这里先给出结论:本题的有向树中只会有一个点的编号
所以,我们需要从
题目代码
注:代码略去缺省源部分,若希望看到缺省源,点击这里。
int a[200005];
int ans;
int maxdep;
int n;
vector < int > v[200005]; // 反向图
vector < int > tppx[200005]; // 正向图,拓扑排序用
int in[200005];
bool flag[200005]; // 判一下有没有被 DFS 求路径,防止拓扑排序之后答案重复
void dfs(int now , int dep)
{
if(now <= n)
{
flag[now] = 1; // 防止这个点被加入拓扑排序
maxdep = max(maxdep , dep);
}
for(int i : v[now])
{
dfs(i , dep + 1);
}
}
signed main()
{
read(n);
readarray(a , 1 , n);
for(int i = 1 ; i <= n ; i++)
{
v[a[i]].push_back(i);
tppx[i].push_back(a[i]);
in[a[i]]++;
}
for(int i = n + 1 ; i <= n * 2 ; i++)
{
maxdep = 0;
dfs(i , 0);
// 路径上的第一个点 ~ 倒数第二个点都会整体向后平移一位
// 所以实际上路径上如果有 x 个点,这一部分答案只有 x - 1
// 所以初始深度设为 0 可以计算出正确答案
// 设成 1 最后 maxdep - 1 其实也行
ans += maxdep;
}
queue < int > q;
for(int i = 1 ; i <= n ; i++)
{
if(!in[i] && !flag[i])
{
q.push(i);
}
}
while(q.size())
{
int now = q.front();
q.pop();
flag[now] = 1;
for(int i : tppx[now])
{
if(--in[i] == 0)
{
q.push(i);
}
}
}
for(int i = 1 ; i <= n ; i++)
{
ans += !flag[i];
// 没进入拓扑排序就是环上的点
// 因为一个环不可能单独拆出一个点入度为 0
}
printnl(ans);
return 0;
}