#define int long long
using namespace std;
signed main(){
这个题一看就是图论题,然后我们观察他的性质,因为一个图论题如果没有什么性质,就是真·不可做......
每个疯子只有一个出度,因此我们YY一下:{
这是一个有向图,所以,我们可以Tarjan,然后我们把点分为强联通分量内,和强联通分量外,然后我们从强联通分量内的点开始走:{
我们一定会走回自己,而且在这条路上不会出轨,那么这个强联通分量里,有了一个环,我们继续看这个环,他不会往外申枝,因此他就是一个
有点特殊的仙人球。
}
我们再走强联通分量外的点:{
他如果不遇到环的话就会一直走下去,因为如果不遇到环,我们每走一步都进入和之前不一样的点,而且每个点都会有且只有一个出度,
所以每个强联通分量外的点都会走到环。
}
综上,这个图大概就是->O<-的类型(对于“联通”的一块,一定有且仅有一个环,其他不在环里的点都通向环,并且他们的路径,只有融合,没有分枝)。
}
所以我们考虑怎么找答案:{
我们先不考虑强联通分量,把那个环拆开,并且以每个环的每个点为根,那么我们发现我们把路径反向之后出现了许多有根树,这样的话....
树dp:{
开数组f[n][]:{
f[i][]:{
他活着,死的最多(少)的人。
}
f[i][]:{
他死了,死的最多(少)的人。
}
记得,我们的状态说的是,最终结果,这个要是理不清会很乱。
}
先考虑转移:{
f[i][]:{
他要是活着,他的爹就必须在最终状态为死,他的儿子也得死。
那么f[i][]=sigma(死了的孩儿们)
}
f[i][]:{
这东西没要求,因为你可以任意调整顺序,得到一大片人头。
那么f[i][]=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=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<='')sum=(sum<<)+(sum<<)+ch-'',ch=getchar();
return sum;
}
struct Via{
int to,next,w;
}c[MAXN<<];
int head[MAXN],t,Ans_Max,Ans_Min,n;
long long f[MAXN][];
int F[MAXN][][];
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]=;
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]=,member[num].push_back(j),special[j]=;
}while(j!=x);
if(member[num].size()==&&Aim[j]!=j){
member[num].clear(),num--,special[j]=;
}
}
}
inline void dfs(int x,int &sum){
int J=;
for(int i=head[x];i;i=c[i].next)
if(c[i].w==&&special[c[i].to]==){
dfs(c[i].to,sum),f[x][]+=f[c[i].to][],f[x][]+=Max(f[c[i].to][],f[c[i].to][]),J++;
}sum++;
if(J==&&special[x]==){ f[x][]=,f[x][]=-Inf; }
else f[x][]+=;
}
inline int Do_It(int No_){
int len=member[No_].size(),sum=;
for(int i=;i<len;i++)dfs(member[No_][i],sum);
if(len==)return f[member[No_][]][];
else{
if(sum==len)return len-;
F[][][]=f[member[No_][]][]<?-:f[member[No_][]][],F[][][]=-,F[][][]=f[member[No_][]][],F[][][]=-;
for(int i=;i<len-;i++){
F[i+][][]=(F[i][][]==-||f[member[No_][i]][]<)?-:(F[i][][]+f[member[No_][i]][]);
F[i+][][]=((F[i][][]==-&&F[i][][]==-)||f[member[No_][i]][]<)?-:(Max(F[i][][],F[i][][])+f[member[No_][i]][]);
F[i+][][]=(F[i][][]==-||f[member[No_][i]][]<)?-:(F[i][][]+f[member[No_][i]][]);
F[i+][][]=((F[i][][]==-&&F[i][][]==-)||f[member[No_][i]][]<)?-:(Max(F[i][][],F[i][][])+f[member[No_][i]][]);
}
register int ans=F[len-][][]+Max(f[member[No_][len-]][],f[member[No_][len-]][]);
if(F[len-][][]!=-)ans=Max(ans,F[len-][][]+f[member[No_][len-]][]);
if(F[len-][][]!=-)ans=Max(ans,F[len-][][]+f[member[No_][len-]][]);
if(F[len-][][]!=-)ans=Max(ans,F[len-][][]+f[member[No_][len-]][]);
return ans;
}
}
inline void Dfs(int x){
int J=;
for(int i=head[x];i;i=c[i].next)
if(c[i].w==&&special[c[i].to]==){
Dfs(c[i].to),f[x][]+=f[c[i].to][],f[x][]+=Min(f[c[i].to][],f[c[i].to][]),J++;
}
if(J==&&special[x]==){ f[x][]=,f[x][]=Inf; }
else f[x][]+=;
}
inline int Cao_It(int No_)
{
register int len=member[No_].size();
for(int i=;i<len;i++)Dfs(member[No_][i]);
if(len==)return f[member[No_][]][];
else{
F[][][]=f[member[No_][]][]>=Inf?Inf:f[member[No_][]][],F[][][]=Inf,F[][][]=f[member[No_][]][],F[][][]=Inf;
for(int i=;i<len-;i++){
F[i+][][]=(F[i][][]==Inf||f[member[No_][i]][]>=Inf)?Inf:(F[i][][]+f[member[No_][i]][]);
F[i+][][]=((F[i][][]==Inf&&F[i][][]==Inf)||f[member[No_][i]][]>=Inf)?Inf:(Min(F[i][][],F[i][][])+f[member[No_][i]][]);
F[i+][][]=(F[i][][]==Inf||f[member[No_][i]][]>=Inf)?Inf:(F[i][][]+f[member[No_][i]][]);
F[i+][][]=((F[i][][]==Inf&&F[i][][]==Inf)||f[member[No_][i]][]>=Inf)?Inf:(Min(F[i][][],F[i][][])+f[member[No_][i]][]);
}
int ans=F[len-][][]+Min(f[member[No_][len-]][],f[member[No_][len-]][]);
if(F[len-][][]!=Inf)ans=Min(ans,F[len-][][]+f[member[No_][len-]][]);
if(F[len-][][]!=Inf)ans=Min(ans,F[len-][][]+f[member[No_][len-]][]);
if(F[len-][][]!=Inf)ans=Min(ans,F[len-][][]+f[member[No_][len-]][]);
return ans;
}
}
inline void Init(){
n=read();for(int i=;i<=n;i++)Aim[i]=read(),add(i,Aim[i],),add(Aim[i],i,);
for(int i=;i<=n;i++)if(!dfn[i])Tarjan(i);
}
inline void Work(){
for(int i=;i<=num;i++)Ans_Max+=Do_It(i);
memset(f,,sizeof(f));for(int i=;i<=num;i++)Ans_Min+=Cao_It(i);
printf("%d %d",Ans_Min,Ans_Max);
}
int main(){
Init();
Work();
return ;
}

【BZOJ 1124】[POI2008] 枪战Maf Tarjan+树dp的更多相关文章

  1. BZOJ 1124: [POI2008]枪战Maf

    1124: [POI2008]枪战Maf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 617  Solved: 236[Submit][Status ...

  2. bzoj 1124 [POI2008]枪战Maf 贪心

    [POI2008]枪战Maf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 741  Solved: 295[Submit][Status][Disc ...

  3. BZOJ 1124: [POI2008]枪战Maf(构造 + 贪心)

    题意 有 \(n\) 个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪. 因此,对于不同的开枪顺序,最后死的人也不同. 问最 ...

  4. 【刷题】BZOJ 1124 [POI2008]枪战Maf

    Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人也不同. ...

  5. 【BZOJ】1124: [POI2008]枪战Maf

    题意 \(n(n < 1000000)\)个人,每个人\(i\)指向一个人\(p_i\),如果轮到\(i\)了且他没死,则他会将\(p_i\)打死.求一种顺序,问死的人最少和最多的数目. 分析 ...

  6. [POI2008]枪战Maf

    [POI2008]枪战Maf 题目 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的 ...

  7. [POI2008]枪战Maf题解

    问题 C: [POI2008]枪战Maf 时间限制: 1 Sec  内存限制: 256 MB 题目描述 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺 ...

  8. BZOJ1124 [POI2008]枪战Maf[贪心(证明未完成)+拓扑排序]

    吐槽:扣了几个小时,大致思路是有了,但是贪心的证明就是不会, 死磕了很长时间,不想想了,结果码代码又不会码.. 深深体会到自己码力很差,写很多行还没写对,最后别人代码全一二十行,要哭了 以下可能是个人 ...

  9. 【BZOJ1124】[POI2008]枪战Maf 贪心+思路题

    [BZOJ1124][POI2008]枪战Maf Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开 ...

随机推荐

  1. 查询表名里含有Bill的表有哪些

    Select Name from Master.dbo.sysobjects where xtype='u' and Name like '%Bill%' order by name

  2. php file_exists中文路径不存在问题

    php的file_exists函数使用中文路径,会显示文件不存在,即使文件已经存在了也会报这个错. 解决方法: <?php $file_name='D://360极速浏览器下载//a.txt'; ...

  3. Java学习笔记九:Java的循环跳转语句

    Java的循环跳转语句 一:Java循环跳转语句之break: 生活中,我们经常会因为某些原因中断既定的任务安排.如在参加 10000 米长跑时,才跑了 500 米就由于体力不支,需要退出比赛.在 J ...

  4. 一笔画问题 南阳acm42(貌似没用到什么算法)

    一笔画问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下 ...

  5. node 分层开发

    app.js var express = require('express');var app = express();app.use('/',require('./control'));app.us ...

  6. linux安装oracle远程客户端

    文章参考:http://blog.csdn.net/caomiao2006/article/details/11901123 感谢博友分享O(∩_∩)O~ 安装oracle 远程客户端(一般情况下本地 ...

  7. javascript对象转为字符串

    function getStringTime(time){ //年 year = time.getFullYear(); //月 month = time.getMonth() if(String(m ...

  8. 关于 poorpool

    poorpool 真名 chenyixiao.是一条傻逼题都不会做的没有脑子的咸鱼. 山西省临汾第一中学 高二 sx省队里最菜的那一个进队靠暴力. 普通的理科生,曾经爱好数学,然而到了高中发现自己所谓 ...

  9. Google序列化库FlatBuffers 1.1发布,及与protobuf的比较

    个人总结: FlatBuffer相对于Protobuffer来讲,优势如下: 1. 由于省去了编解码的过程,所以从速度上快于Protobuffer,个人测试结果100w次编解码,编码上FlatBuff ...

  10. 对 a = [lambda : x for x in range(3)] 的理解

    上面的语句创建了一个列表 a ,其中有三个元素,每个元素都是一个 lambda 匿名函数. >>> a = [lambda : x for x in range(3)] >&g ...