BZOJ 3037 创世纪

Posted on 2017-03-18 22:55  ziliuziliu  阅读(214)  评论(0编辑  收藏  举报

我勒个去怎么这么长。。。。。

求基环森林的最小点覆盖。注意这里是有向的。那么我们考虑加不加多的那条边,对每个连通块dp两遍就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxv 1000050
#define inf 1000000007
using namespace std;
int n,a[maxv],dx[maxv],dy[maxv],pos[maxv],dp[maxv][3],cnt=0,father[maxv],ans=0;
bool flag[maxv];
struct pnt
{
    int id,father,rank;
}p[maxv];
struct edge
{
    int x,y,father,root;
}e[maxv];
queue <int> q;
int read()
{
    char ch;int data=0;
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9') 
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data;
}
bool cmp1(pnt x,pnt y)
{
    if (x.father!=y.father) return x.father<y.father;
    return x.rank<y.rank;
}
bool cmp2(edge x,edge y) {return x.father<y.father;}
int getfather(int x)
{
    if (father[x]==x) return x;
    father[x]=getfather(father[x]);return father[x];
}
void topu_sort()
{
    for (int i=1;i<=n;i++)
    {
        if (!dy[i]) q.push(i);
        p[i].father=getfather(i);
    }
    while (!q.empty())
    {
        int head=q.front();q.pop();
        dy[a[head]]--;if (!dy[a[head]]) {p[a[head]].rank=p[head].rank+1;q.push(a[head]);}
    }
    sort(p+1,p+n+1,cmp1);sort(e+1,e+cnt+1,cmp2);
}
void tree_dp(int x,int l,int r)
{
    for (int i=l;i<=r;i++) {pos[p[i].id]=inf;flag[p[i].id]=false;dp[p[i].id][1]=dp[p[i].id][2]=0;}
    int i;dp[0][1]=dp[0][2]=inf;
    for (i=l;!p[i].rank;i++)
    {
        int v=p[i].id;
        if (v!=x) dp[v][1]=inf;else dp[v][1]=0;dp[v][2]=1;
        if (v!=a[v])
        {
            if (dp[v][1]<dp[v][2]) {dp[a[v]][1]+=dp[v][1];pos[a[v]]=min(pos[a[v]],dp[v][2]-dp[v][1]);}
            else {dp[a[v]][1]+=dp[v][2];flag[a[v]]=true;}
            dp[a[v]][2]+=min(dp[v][1],dp[v][2]);
        }
        dp[v][1]=min(dp[v][1],inf);dp[v][2]=min(dp[v][2],inf);
    }
    for (;i<=r;i++)
    {
        int v=p[i].id;
        if ((v!=x) && (!flag[v])) dp[v][1]+=pos[v];dp[v][2]++;
        if (v!=a[v])
        {
            if (dp[v][1]<dp[v][2]) {dp[a[v]][1]+=dp[v][1];pos[a[v]]=min(pos[a[v]],dp[v][2]-dp[v][1]);}
            else {dp[a[v]][1]+=dp[v][2];flag[a[v]]=true;}
            dp[a[v]][2]+=min(dp[v][1],dp[v][2]);
        }
        dp[v][1]=min(dp[v][1],inf);dp[v][2]=min(dp[v][2],inf);
    }
}
int main()
{ 
    n=read();for (int i=1;i<=n;i++) {father[i]=i;p[i].id=i;}
    for (int i=1;i<=n;i++)
    {
        a[i]=read();
        int f1=getfather(i),f2=getfather(a[i]);
        if (f1==f2) {e[++cnt].x=i;e[cnt].y=a[i];}
        else {dx[i]++;dy[a[i]]++;father[f1]=f2;}
    }
    for (int i=1;i<=cnt;i++) e[i].father=getfather(e[i].x); 
    topu_sort();
    int l=1,r=1,ret=0;
    while (l<=n)
    {
        int mn=inf;r=l;while (p[r+1].father==p[l].father) r++;
        int i;ret++;
        tree_dp(-1,l,r);mn=min(mn,min(dp[e[ret].x][1],dp[e[ret].y][2]));
        tree_dp(e[ret].y,l,r);mn=min(mn,dp[e[ret].x][2]);
        ans+=mn;l=r+1;
    }
    printf("%d\n",n-ans);
    return 0;
}