【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;
}

 

posted @ 2017-08-02 14:49  TS_Hugh  阅读(323)  评论(0编辑  收藏  举报