【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; }
苟利国家生死以, 岂因祸福避趋之。