zoukankan      html  css  js  c++  java
  • 【BZOJ 1124】[POI2008] 枪战Maf Tarjan+树dp

    #define int long long 
    using namespace std;
    signed main(){
      这个题一看就是图论题,然后我们观察他的性质,因为一个图论题如果没有什么性质,就是真·不可做......
      每个疯子只有一个出度,因此我们YY一下:{
        这是一个有向图,所以,我们可以Tarjan,然后我们把点分为强联通分量内,和强联通分量外,然后我们从强联通分量内的点开始走:{
          我们一定会走回自己,而且在这条路上不会出轨,那么这个强联通分量里,有了一个环,我们继续看这个环,他不会往外申枝,因此他就是一个
          有点特殊的仙人球。
        }
        我们再走强联通分量外的点:{
          他如果不遇到环的话就会一直走下去,因为如果不遇到环,我们每走一步都进入和之前不一样的点,而且每个点都会有且只有一个出度,
          所以每个强联通分量外的点都会走到环。
        }
        综上,这个图大概就是->O<-的类型(对于“联通”的一块,一定有且仅有一个环,其他不在环里的点都通向环,并且他们的路径,只有融合,没有分枝)。
      }
      所以我们考虑怎么找答案:{
        我们先不考虑强联通分量,把那个环拆开,并且以每个环的每个点为根,那么我们发现我们把路径反向之后出现了许多有根树,这样的话....
        树dp:{
          开数组f[n][2]:{
            f[i][1]:{
              他活着,死的最多(少)的人。
            }
            f[i][0]:{
              他死了,死的最多(少)的人。
            }
            记得,我们的状态说的是,最终结果,这个要是理不清会很乱。
          }
          先考虑转移:{
            f[i][1]:{
              他要是活着,他的爹就必须在最终状态为死,他的儿子也得死。
              那么f[i][1]=sigma(死了的孩儿们)
            }
            f[i][0]:{
              这东西没要求,因为你可以任意调整顺序,得到一大片人头。
              那么f[i][0]=sigma(Max(死儿子,活儿子));
            }
          }
          此外还有两个坑点:{
            入度为零的点,必活;儿子里面存在入度为零的点,必死。
            这样的话我们赋Inf来表示不可行。(...................)
          }
        }
        然后我们得到了,每个环内的点活或死所得到的最大(最小)值,然后我们根据刚才的结论(活活不相挨),进行线性dp:{
          在这之前我们当然要把环上的点连续地放到一个数列里,然而这是个环,我们数列的首项和末项也是有瓜葛的,那么我们就需要再开一维表示
          第一个点死活。
          这里还有个坑:{
            对于一个没有枝杈的环,那么他至少剩一个;
            但是对于自换,必死。
          }
        }
      }
      这样我们写出这样一个屎代码就能过了,你要是去波兰源网main.edu.pl/en,或者bzoj的话,递归函数一定要开inline因为有两个原来有但是我们没有的点,
      就是递归层数1000000,栈空间炸到出屎。
    }
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #define MAXN 1000010
    #define Inf 0x3f3f3f3f
    using namespace std;
    inline int read(){
      int sum=0;char ch=getchar();
      while(ch<'0'||ch>'9')ch=getchar();
      while(ch>='0'&&ch<='9')sum=(sum<<1)+(sum<<3)+ch-'0',ch=getchar();
      return sum;
    }
    struct Via{
      int to,next,w;
    }c[MAXN<<1];
    int head[MAXN],t,Ans_Max,Ans_Min,n;
    long long f[MAXN][2];
    int F[MAXN][2][2];
    int Aim[MAXN];
    int dfn[MAXN],low[MAXN],stack[MAXN],top,Time,num;
    bool in[MAXN],special[MAXN];
    vector<int> member[MAXN];
    inline long long Min(long long x,long long y){
      return x<y?x:y;
    }
    inline long long Max(long long x,long long y){
      return x>y?x:y;
    }
    inline void add(int x,int y,int z){
      c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].w=z;
    }
    inline void Tarjan(int x){
      dfn[x]=low[x]=++Time,stack[++top]=x,in[x]=1;
      for(int i=head[x];i;i=c[i].next)
      if(c[i].w){
        if(!dfn[c[i].to]){
          Tarjan(c[i].to),low[x]=Min(low[x],low[c[i].to]);
        }else if(in[c[i].to])
          low[x]=Min(low[x],dfn[c[i].to]);
      }
      if(dfn[x]==low[x]){
        int j;num++;
        do{
          j=stack[top--],in[j]=0,member[num].push_back(j),special[j]=1;
        }while(j!=x);
        if(member[num].size()==1&&Aim[j]!=j){
          member[num].clear(),num--,special[j]=0;
        }
      }
    }
    inline void dfs(int x,int &sum){
      int J=0;
      for(int i=head[x];i;i=c[i].next)
      if(c[i].w==0&&special[c[i].to]==0){
        dfs(c[i].to,sum),f[x][1]+=f[c[i].to][0],f[x][0]+=Max(f[c[i].to][1],f[c[i].to][0]),J++;
      }sum++;
      if(J==0&&special[x]==0){ f[x][1]=0,f[x][0]=-Inf; }
      else f[x][0]+=1;
    }
    inline int Do_It(int No_){
      int len=member[No_].size(),sum=0;
      for(int i=0;i<len;i++)dfs(member[No_][i],sum);
      if(len==1)return f[member[No_][0]][0];
      else{
        if(sum==len)return len-1;
        F[1][1][1]=f[member[No_][0]][1]<0?-1:f[member[No_][0]][1],F[1][0][1]=-1,F[1][0][0]=f[member[No_][0]][0],F[1][1][0]=-1;
        for(int i=1;i<len-1;i++){
          F[i+1][1][1]=(F[i][0][1]==-1||f[member[No_][i]][1]<0)?-1:(F[i][0][1]+f[member[No_][i]][1]);
          F[i+1][0][1]=((F[i][0][1]==-1&&F[i][1][1]==-1)||f[member[No_][i]][0]<0)?-1:(Max(F[i][1][1],F[i][0][1])+f[member[No_][i]][0]);
          F[i+1][1][0]=(F[i][0][0]==-1||f[member[No_][i]][1]<0)?-1:(F[i][0][0]+f[member[No_][i]][1]);
          F[i+1][0][0]=((F[i][0][0]==-1&&F[i][1][0]==-1)||f[member[No_][i]][0]<0)?-1:(Max(F[i][1][0],F[i][0][0])+f[member[No_][i]][0]);
        }
        register int ans=F[len-1][0][0]+Max(f[member[No_][len-1]][1],f[member[No_][len-1]][0]);
        if(F[len-1][0][1]!=-1)ans=Max(ans,F[len-1][0][1]+f[member[No_][len-1]][0]);
        if(F[len-1][1][0]!=-1)ans=Max(ans,F[len-1][1][0]+f[member[No_][len-1]][0]);
        if(F[len-1][1][1]!=-1)ans=Max(ans,F[len-1][1][1]+f[member[No_][len-1]][0]);
        return ans;
      }
    }
    inline void Dfs(int x){
      int J=0;
      for(int i=head[x];i;i=c[i].next)
      if(c[i].w==0&&special[c[i].to]==0){
        Dfs(c[i].to),f[x][1]+=f[c[i].to][0],f[x][0]+=Min(f[c[i].to][1],f[c[i].to][0]),J++;
      }
      if(J==0&&special[x]==0){ f[x][1]=0,f[x][0]=Inf; }
      else f[x][0]+=1;
    }
    inline int Cao_It(int No_)
    {
      register int len=member[No_].size();
      for(int i=0;i<len;i++)Dfs(member[No_][i]);
      if(len==1)return f[member[No_][0]][0];
      else{
        F[1][1][1]=f[member[No_][0]][1]>=Inf?Inf:f[member[No_][0]][1],F[1][0][1]=Inf,F[1][0][0]=f[member[No_][0]][0],F[1][1][0]=Inf;
        for(int i=1;i<len-1;i++){
          F[i+1][1][1]=(F[i][0][1]==Inf||f[member[No_][i]][1]>=Inf)?Inf:(F[i][0][1]+f[member[No_][i]][1]);
          F[i+1][0][1]=((F[i][0][1]==Inf&&F[i][1][1]==Inf)||f[member[No_][i]][0]>=Inf)?Inf:(Min(F[i][1][1],F[i][0][1])+f[member[No_][i]][0]);
          F[i+1][1][0]=(F[i][0][0]==Inf||f[member[No_][i]][1]>=Inf)?Inf:(F[i][0][0]+f[member[No_][i]][1]);
          F[i+1][0][0]=((F[i][0][0]==Inf&&F[i][1][0]==Inf)||f[member[No_][i]][0]>=Inf)?Inf:(Min(F[i][1][0],F[i][0][0])+f[member[No_][i]][0]);
        }
        int ans=F[len-1][0][0]+Min(f[member[No_][len-1]][1],f[member[No_][len-1]][0]);
        if(F[len-1][0][1]!=Inf)ans=Min(ans,F[len-1][0][1]+f[member[No_][len-1]][0]);
        if(F[len-1][1][0]!=Inf)ans=Min(ans,F[len-1][1][0]+f[member[No_][len-1]][0]);
        if(F[len-1][1][1]!=Inf)ans=Min(ans,F[len-1][1][1]+f[member[No_][len-1]][0]);
        return ans;
      }
    }
    inline void Init(){
      n=read();for(int i=1;i<=n;i++)Aim[i]=read(),add(i,Aim[i],1),add(Aim[i],i,0);
      for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
    }
    inline void Work(){
      for(int i=1;i<=num;i++)Ans_Max+=Do_It(i);
      memset(f,0,sizeof(f));for(int i=1;i<=num;i++)Ans_Min+=Cao_It(i);
      printf("%d %d",Ans_Min,Ans_Max);
    }
    int main(){
      Init();
      Work();
      return 0;
    }
  • 相关阅读:
    ChukWa入门1
    asp.net常用代码集锦
    泛型讲解
    深入宠物店PetShopSQLServerDAL数据访问与SampleDuwamish比较
    写有效率的SQL查询(转载)
    VisualStudio2005技巧集合
    iptables总结【转载】
    vmware workstation 如何注册
    4.继承
    Linux系统下源代码包方式 安装前准备[1]
  • 原文地址:https://www.cnblogs.com/TSHugh/p/7273841.html
Copyright © 2011-2022 走看看