zoukankan      html  css  js  c++  java
  • BZOJ1124 POI2008枪战Maf(环套树+贪心)

      每个点出度都为1,可以发现这张图其实是个环套树森林,树中儿子指向父亲,环上边同向。

      首先自环肯定是没救的,先抬出去。

      要使死亡人数最多的话,显然若一个点入度为0其不会死亡,而一个孤立的环至少会留下一个点。对于环套树,若某个点有子树,可以以瞄准它的点为起点,每个点都被在环上瞄准他的点所击中。这样就剩下了很多棵树,除叶子节点的点都会死亡。

      死亡人数最少似乎同样可以贪心,虽然我没这么写。可以发现最后存活下来的人之间一定不存在瞄准关系,否则必有一个死亡。并且只要最后存活下来的人之间不存在瞄准关系(且不被瞄准的存活),一定有方案使这些人最终存活下来,对于一个连通块人的死亡只要按照拓扑逆序开枪即可(使孤立环全部死亡是办不到的,但显然要使死亡人数最少我们不会这样干)。于是求一下环套树的包含所有叶子节点的最大独立集即可。

      细节挺多,在luoguA了,bzoj跑了好长时间之后wa掉了,不知道哪写挂了啊。

      upd:发现是一些奇怪的地方爆了int……现在A掉辣!

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    #define N 1000010
    int n,a[N],id[N],degree[N],dfn[N],low[N],stk[N],set[N],size[N];
    int top=0,cnt=0,t=0;
    long long ans1=0,ans2=0,f[N][2],g[N][2][2];
    bool flag[N],isroot[N];
    int p[N];
    struct data{int to,nxt;
    }edge[N];
    void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
    void tarjan(int k)
    {
        dfn[k]=low[k]=++cnt;
        stk[++top]=k;flag[k]=1;
        if (a[a[k]]!=a[k])
        if (!dfn[a[k]]) tarjan(a[k]),low[k]=min(low[k],low[a[k]]);
        else if (flag[a[k]]) low[k]=min(low[k],dfn[a[k]]);
        if (dfn[k]==low[k])
        {
            t++;
            while (stk[top]!=k)
            {
                set[stk[top]]=t;
                size[t]++;
                flag[stk[top]]=0;
                top--;
            }
            set[k]=t;size[t]++;flag[k]=0;top--;
        }
    }
    void dfs(int k)
    {
        f[k][1]=1,f[k][0]=0;
        if (!degree[k]) f[k][0]=-n;
        for (int i=p[k];i;i=edge[i].nxt)
        if (size[set[edge[i].to]]==1)
        {
            dfs(edge[i].to);
            f[k][0]+=max(f[edge[i].to][0],f[edge[i].to][1]);
            f[k][1]+=f[edge[i].to][0];
        }
    }
    int main()
    {
        n=read();
        for (int i=1;i<=n;i++) 
        {
            a[i]=read();
            if (a[i]==i) ans1++;
            else degree[a[i]]++;
        }
        for (int i=1;i<=n;i++)
        if (!dfn[i]&&a[i]!=i) tarjan(i);
        for (int i=1;i<=n;i++)
        if (degree[i]&&size[set[i]]==1) ans1++;
        memset(flag,0,sizeof(flag));
        for (int i=1;i<=n;i++)
        if (size[set[i]]>1&&degree[i]>1) flag[set[i]]=1;
        for (int i=1;i<=t;i++)
        if (size[i]>1) ans1+=size[i]-1+flag[i];
        t=0;
        for (int i=1;i<=n;i++)
        if (a[i]!=i) addedge(a[i],i);
        for (int i=1;i<=n;i++)
        if (size[set[i]]>1||a[a[i]]==a[i]&&a[i]!=i) isroot[i]=1;
        for (int i=1;i<=n;i++)
        if (isroot[i]) dfs(i);
        memset(dfn,0,sizeof(dfn));
        for (int i=1;i<=n;i++)
        if (isroot[i]&&!dfn[i]) 
        if (a[a[i]]==a[i]) ans2+=max(f[i][0],f[i][1]);
        else
        {
            int x=i,t=0;
            while (a[x]!=i) dfn[x=a[x]]=1,id[++t]=x;
            id[++t]=i;dfn[i]=1;
            for (int j=1;j<=t;j++) g[i][0][0]=g[i][0][1]=g[i][1][0]=g[i][1][1]=0;
            g[1][0][0]=f[id[1]][0],g[1][1][1]=f[id[1]][1];
            g[1][1][0]=g[1][0][1]=-n;
            for (int j=2;j<=t;j++)
            {
                g[j][0][0]=max(g[j-1][0][0],g[j-1][1][0])+f[id[j]][0];
                g[j][0][1]=max(g[j-1][0][1],g[j-1][1][1])+f[id[j]][0];
                g[j][1][0]=g[j-1][0][0]+f[id[j]][1];
                g[j][1][1]=g[j-1][0][1]+f[id[j]][1];
            }
            ans2+=max(g[t][0][0],max(g[t][0][1],g[t][1][0]));
        }
        ans2=n-ans2;
        cout<<ans2<<' '<<ans1;
        return 0;
    }
  • 相关阅读:
    python 的 类属性 与 实例属性
    python 的 append 和 extend
    机器学习的最佳入门学习资源
    各种排序算法探讨与实现
    C++基础:C++中vector使用简介
    C++基础:C++中的explicit关键字
    差分约束(poj 1201
    codeforeces 547C
    2015 7月 做题记录
    set&map
  • 原文地址:https://www.cnblogs.com/Gloid/p/9403624.html
Copyright © 2011-2022 走看看