【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 echo和print 语句
PHP echo 和 print 语句 PHP 是通过 print 和 echo 语句来动态输出 HTML 内容,虽然 print 和 echo 语句两者的功能几乎是完全一样,但是还是有一点差别的. ...
- Hadoop(25)-高可用集群配置,HDFS-HA和YARN-HA
一. HA概述 1. 所谓HA(High Available),即高可用(7*24小时不中断服务). 2. 实现高可用最关键的策略是消除单点故障.HA严格来说应该分成各个组件的HA机制:HDFS的HA ...
- scala成长之路(2)对象和类
scala提供了一种特殊的定义单例的方法:object关键字 scala> object Shabi{ | val age = 0 | val name = "shabi" ...
- python 装饰器 生成及原里
# 装饰器形成的过程 : 最简单的装饰器 有返回值的 有一个参数 万能参数 # 装饰器的作用 # 原则 :开放封闭原则 # 语法糖 :@ # 装饰器的固定模式 #不懂技术 import time # ...
- 学习RUNOOB.COM进度二
MongoDB 概念解析 SQL术语/概念 MongoDB术语/概念 解释/说明 database database 数据库 table collection 数据库表/集合 row document ...
- FPGA算法学习(1) -- Cordic(圆周系统之向量模式)
旋转模式用来解决三角函数,实现极坐标到直角坐标的转换,基础理论请参考Cordic算法--圆周系统之旋转模式.那么,向量模式则用来解决反三角函数的问题,体现的应用主要是直角坐标向极坐标转换,即已知一点的 ...
- AtCoder AGC028-F:Reachable Cells
越来越喜欢AtCoder了,遍地都是神仙题. 题意: 给定一个\(N\)行\(N\)列的迷宫,每一个格子要么是障碍,要么是空地.每一块空地写着一个数码.在迷宫中,每一步只允许向右.向下走,且只能经过空 ...
- .net Core错误记录
检测到包降级 打开Nuget,找到对应的包,Microsoft.NetCore.App 此时提示'已被SDK隐式引用,若要更新该包,请更新所属的SDK' 啥鸟意思??? 不管,直接解决,首先,安装对应 ...
- P2678 跳石头(二分答案)
P2678 跳石头 题目背景 一年一度的“跳石头”比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间 ...
- 美年健康股票成交量和K线关系
看下美年健康的股票,这次主要是研究下成交量和K线的关系,以最后5天为例子,股票下跌成交量降低,说明抛压很小,在最后3天,价格突破的时候,成交量是平时的两倍,说明有机构买入, 业绩部分还可以,全民健身是 ...