【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个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开 ...
随机推荐
- php导出excel长数字串显示为科学计数方法与最终解决方法
1.设置单元格为文本 $objPHPExcel = new PHPExcel(); $objPHPExcel->setActiveSheetIndex(0); $objPHPExcel-> ...
- Scrapy框架的基本使用
安装 pip install scrapy 基础使用 1. 创建一个工程:scrapy startproject 2. 在工程目录下创建一个爬虫文件 cd 工程 scrapy genspider 爬虫 ...
- Python学习之property
Python中使用Property函数可以将类中的函数当作属性来调用. 案例 __metaclass__=type class Rectangle: def __init__(self): self. ...
- 关于springboot 打包问题 jar包和 war包
起因:项目开发完成 需要打包部署了 发现自己不会打包 那么开始网上学习打包? 那么怎么来打包那? 我们以前没有采用springboot 时候我们都是直接将项目打成war包形式 然后放到tomc ...
- Oracle11.2.0.3 RAC配置ODBC成功案例记录
最终使用字符串如下: String url="jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = scan- ...
- adb常用命令(手机测试)
ADB安装与常用命令详解 一.ADB意义 adb的全称为Android Debug Bridge,就是起到 ...
- vue路由文档笔记
引入router this.$router 和 router 使用起来完全一样.我们使用 this.$router 的原因是我们并不想在每个独立需要封装路由的组件中都导入路由 可以在任何组件内通过 t ...
- ssh以bash登录的配置
因ssh登录时不会加载.bashrc而是加载.bash_profile,所以以ssh的默认登录不会是bash,只要在.bash_profile中添加以下代码即可: if [ -f ~/.bashrc ...
- centos7安装python3.7
Centos7安装Python3的方法 由于centos7原本就安装了Python2,而且这个Python2不能被删除,因为有很多系统命令,比如yum都要用到. [root@VM_105_217_ ...
- C++ 学习笔记之——文件操作和文件流
1. 文件的概念 对于用户来说,常用到的文件有两大类:程序文件和数据文件.而根据文件中数据的组织方式,则可以将文件分为 ASCII 文件和二进制文件. ASCII 文件,又称字符文件或者文本文件,它的 ...