展翅翱翔之时 (はばたきのとき)

题目

解题思路:

贪心
或者:
我们把这种每个点入度(或者出度)为1的点称为有向环套树。显然,给出的图是一个环套树森林。我们讨论单个环套树的情况。

如果要两两可以互相交互,那么就是形成强连通。

只有一种结构可以达成:环。

所以说,我们需要找找出每个环套树的最长链。

首先找出环,然后环上的树上有多个孩子的点贪心保留权值大的点,这样变成了外向链。

然后枚举环上的每个点,把这个环破开,然后计算最长的链(你可以想象一下“6”这个数字,对就是这个意思)。

森林可能不连通,所以分别处理。最后复杂度是 O(n)
想学一下这种于是就用了这种写法

Accepted code:

#include<bits/stdc++.h>
#define N 100005
typedef long long ll;
using namespace std;
struct Edge{
    int u,v,next;
}G[N<<1];
int head[N],tot=0,vis[N],c[N],q[N],top=0,s[N],n,cnt;
ll sum;
inline int read()
{
    int f=0,ag=1;char c=getchar();
    while(!isdigit(c)) {if(c=='-') ag=-1; c=getchar();}
    while(isdigit(c)) f=(f<<1)+(f<<3)+c-48,c=getchar();
    return f*ag;
}
inline void add(int u,int v)
{
    G[++tot].u=u;G[tot].v=v;G[tot].next=head[u];head[u]=tot;
}
void dfs(int u)
{
    vis[u]=1;q[++top]=u;s[u]=1;
    int mv=0,mst=0,mn=0;ll ret=0;
    for(int i=head[u];i;i=G[i].next)
    {
        int v=G[i].v; ret+=c[v];
        if(c[v]>mn) 
            c[mv]=0,mst=mn,mn=c[v],c[v]-=mst,mv=v;
        else
            {if(c[v]>mst) mst=c[v],c[mv]=mn-mst; c[v]=0;}
    }
    ret-=mn;
    for(int i=head[u];i;i=G[i].next)
    {
        int v=G[i].v;
        if(s[v])
        {
            int minv=(1<<30);
            if(v==1&&top==n) {puts("0");exit(0);}
            for(int i=top;;i--)
            {
                minv=min(minv,c[q[i]]);
                if(q[i]==v) break;
            }
            ret+=minv;
        }
        else if(!vis[v]) dfs(v);
    }
    sum+=ret; top--; s[u]=0;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int v=read();c[i]=read();
        add(v,i);
    }
    for(int i=1;i<=n;i++)
        if(!vis[i]) dfs(i);
    return printf("%lld\n",sum)&0;
}

猜你喜欢

转载自blog.csdn.net/qq_39798042/article/details/81675221