zoukankan      html  css  js  c++  java
  • 仙人掌学习笔记

    定义:

    • 是一个连通图。
    • 每一条边最多属于一个简单环。

    做法:

    DFS树

    根据 (tarjan) 的过程,得到一个(DFS)树,同时也可以可得到每个点属于哪一个环(或不属于任何一个环)。对于只属于树的边,进行树形DP;对于环上的边,在环上DP。适用于仅需操作一次的简单仙人掌DP。
    代码框架如下(以求仙人掌直径为例):

    void dp(int x,int y) {//环上DP
    	cnt=0; for(int i=y;i!=x;i=fa[i]) a[++cnt]=f[i]; a[++cnt]=f[x];//处理出环上的点
    	for(int i=1;i<=cnt/2;i++)	swap(a[i],a[cnt-i+1]);
    	for(int i=cnt+1;i<=cnt+cnt/2;i++) a[i]=a[i-cnt];//复制原串
    	q[F=E=1]=1;//单调队列优化DP
    	for(int i=2;i<=cnt+cnt/2;i++) {
    		if(F<=E&&i-q[F]>cnt/2)	++F;
    		ans=Max(ans,a[q[F]]+a[i]+i-q[F]);
    		while(F<=E&&a[i]-i>=a[q[E]]-q[E]) --E;
    		q[++E]=i;
    	}
    	for(int i=2;i<=cnt;i++) f[x]=Max(f[x],a[i]+Min(i-1,cnt-i+1));//更新
    }
    
    void dfs(int u,int ff) {//模拟tarjan的过程
    	dfn[u]=low[u]=++idx,f[u]=0;//f[i]为DP的状态
    	for(int i=0;i<e[u].size();i++) {//枚举每条出边
    		if(e[u][i]==ff)	continue;//判掉父亲
    		if(!dfn[e[u][i]])//未访问过,加边建DFS树
    			fa[e[u][i]]=u,dfs(e[u][i],u),low[u]=Min(low[u],low[e[u][i]]);
    		else low[u]=Min(low[u],dfn[e[u][i]]);//更新该节点能回到的最早的点
    		if(low[e[u][i]]>dfn[u])//进行简单的树形DP
    			ans=Max(ans,f[u]+f[e[u][i]]+1),f[u]=Max(f[u],f[e[u][i]]+1);
    	}
    	for(int i=0;i<e[u].size();i++)//查找仙人掌上的儿子但不是DFS树上的儿子的点,即形成环
    		if(e[u][i]!=ff&&fa[e[u][i]]!=u&&dfn[e[u][i]]>dfn[u]) dp(u,e[u][i]);//进行环上DP
    }
    

    例题:
    SHOI2008 cactus仙人掌图
    小C的独立集
    HNOI2009 无归岛


    圆方树

    (听说广义圆方树可以处理一些图的问题,如 【CF Round #278】Tourists, APIO2018 Duathlon 铁人两项)

    留坑待填

  • 相关阅读:
    BZOJ4008: [HNOI2015]亚瑟王
    BZOJ4260: Codechef REBXOR
    BZOJ4408: [Fj Winter Camp 2016]神秘数
    BZOJ4010: [HNOI2015]菜肴制作
    ccc2016
    BZOJ3884: 上帝与集合的正确用法
    BZOJ1017: [JSOI2008]魔兽地图DotR
    BZOJ1011: [HNOI2008]遥远的行星
    BestCoder Round #73
    hdu4035(概率dp)
  • 原文地址:https://www.cnblogs.com/daniel14311531/p/10246731.html
Copyright © 2011-2022 走看看