每个点出度都为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&°ree[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; }