【BZOJ 1124】[POI2008] 枪战Maf Tarjan+树dp
#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的更多相关文章
- BZOJ 1124: [POI2008]枪战Maf
1124: [POI2008]枪战Maf Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 617 Solved: 236[Submit][Status ...
- bzoj 1124 [POI2008]枪战Maf 贪心
[POI2008]枪战Maf Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 741 Solved: 295[Submit][Status][Disc ...
- BZOJ 1124: [POI2008]枪战Maf(构造 + 贪心)
题意 有 \(n\) 个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪. 因此,对于不同的开枪顺序,最后死的人也不同. 问最 ...
- 【刷题】BZOJ 1124 [POI2008]枪战Maf
Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人也不同. ...
- 【BZOJ】1124: [POI2008]枪战Maf
题意 \(n(n < 1000000)\)个人,每个人\(i\)指向一个人\(p_i\),如果轮到\(i\)了且他没死,则他会将\(p_i\)打死.求一种顺序,问死的人最少和最多的数目. 分析 ...
- [POI2008]枪战Maf
[POI2008]枪战Maf 题目 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的 ...
- [POI2008]枪战Maf题解
问题 C: [POI2008]枪战Maf 时间限制: 1 Sec 内存限制: 256 MB 题目描述 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺 ...
- BZOJ1124 [POI2008]枪战Maf[贪心(证明未完成)+拓扑排序]
吐槽:扣了几个小时,大致思路是有了,但是贪心的证明就是不会, 死磕了很长时间,不想想了,结果码代码又不会码.. 深深体会到自己码力很差,写很多行还没写对,最后别人代码全一二十行,要哭了 以下可能是个人 ...
- 【BZOJ1124】[POI2008]枪战Maf 贪心+思路题
[BZOJ1124][POI2008]枪战Maf Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开 ...
随机推荐
- 神级编辑器 sublime text 和 神级插件 emmet
h1{foo}和a[href=#] 生成如下代码 <h1>foo</h1> <a href="#"></a> 嵌套的使用 > ...
- PHP中的面向对象魔术方法大全
1.__construct 构造方法 2.__destruct 析构方法 3.__get 获取成员值 4.__set 设定成员值 5.__isset 判断成员值 6.__unset unset成员 ...
- C语言实现二分查找
二分查找优势:比顺序查找更有效率 特点:元素按顺序排列 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include ...
- (数据科学学习手札05)Python与R数据读入存出方式的总结与比较
在数据分析的过程中,外部数据的导入和数据的导出是非常关键的部分,而Python和R在这方面大同小异,且针对不同的包或模块,对应着不同的函数来完成这部分功能: Python 1.TXT文件 导入: 以某 ...
- java图片识别 [Tesseract-OCR]
以下链接包含,安装包及程序运行需要的jar 包,中文资源包. 中文包使用方式:找到tessdata安装目录(我本地:C:\Program Files (x86)\Tesseract-OCR\tessd ...
- octave简易操作
语言以分号;结尾if for while等语句后用,来承接关系if ,elseif ,else ,end;for i=1:10,end;while a>3 ,end; while true, ...
- 【转】手把手教你:Ubuntu14+apache2+django1.7+python2.7下网页/网站部署
本人亲自尝试了网上众多的部署网页/网站方法,绝大多数都未能试验成功,这次的项目光部署这块遇到了很多问题,大概耗费了我一个星期. 本着:王道论坛中的赠人玫瑰,手留余香的精神.我把自己一路所走的历程发布出 ...
- FireDAC 连接Access (accdb)数据库
FireDAC可以方便连接数据库,但是要连接新版本的accdb数据库,要注意这样的事项(以Office2010版为例) 安装Office2010 x86版,注意,不能安装x64版,因为Delphi I ...
- 9.Mongodb与python交互
1.与python交互 点击查看官方文档 安装python包 进入虚拟环境 sudo pip install pymongo 或源码安装 python setup.py 引入包pymongo impo ...
- OrCAD把原理图中的器件添加到原理图库
1. 在使用OrCAD的时候,有时需要把别人的原理图里面的器件添加到自己的原理图库,方便以后使用,具体操作如下,依次选择Design Cache---元器件--Copy 2. 选中要存放的原理图库,鼠 ...