BZOJ 1023 [SHOI2008]cactus仙人掌图:圆方树+单调队列DP

题目传送门

题意:给出一个仙人掌图,边权都为1,求其直径。

仙人掌图:无向图的每条边至多存在于一个简单环中。

仙人掌图直径:Max(dis(u,v)) 1<= u < v <=n。dis(u,v)是u、v之间的最短路径。

题解:

先考虑退化情况:仙人掌退化为一棵树。很显然可以通过一个树形DP来解决,讨论路径的形态:1、分跨在一个点的两个子树中2、是一条从根节点到叶子节点的路径。每个点需要两个dp值记录子树中到叶子的最长和次长路径。然后进行简单的转移即可。
非退化情况:对仙人掌图求出圆方树。
对于圆点而言,处理方法和上边的一样。
对于方点而言,问题转化为:给出一个环基树,已知环上顺次都是那些点、以及每个点向外侧延伸的最大链长。求出环基树的直径,这个问题有一种极其经典的RMQ的有限队列解法,可以做到O(环长)的复杂度。因此总体复杂度是O(n)

圆方树详细资料:WC2017课件传送门

由于本人不喜欢破环操作。。。对环基树直径采用了分类讨论的方法:环上路径是否经过环的根。也可以做到O(环长)的复杂度。
我的tarjan求点双不需要处理长度=2的无效环问题。
这个题交了十几次才AC掉…已经不省人事了…然而这只是毒瘤入门题,看来想成为毒瘤是需要付出巨大的努力…………

Code:

#include
#define pb(x) push_back(x)
using namespace std;
const int maxn = 1e5+100;
vector<int>E1[maxn],ET[2*maxn],LenT[2*maxn];
int dfn[maxn],fa[2*maxn],len[maxn*2],dfs_clock;
bool inCircle[maxn];
int m,n,ans,N;
int dp[maxn][2];
int Q[maxn],head,tail,Max[maxn*2];;
inline void addEdge1(int x,int y){
    E1[x].pb(y);
}
inline void addEdgeT(int x,int y,int w){
    ET[x].pb(y);
    LenT[x].pb(w);
}
void input(){
    scanf("%d%d",&n,&m);
    N=n;
    for (int i=0;iint k,u;
        scanf("%d%d",&k,&u);
        for (int j=1;jint v;
            scanf("%d",&v);
            addEdge1(u,v);
            addEdge1(v,u);
            u = v;
        }
    }
}
void tarjan(int u){
    dfn[u] = ++ dfs_clock;
    for (int i=0;iint v = E1[u][i];
        if (v==fa[u])continue;
        if (!dfn[v]){
            fa[v] =u;
            tarjan(v);
        }else if (dfn[v]0);
            int temp = u;
            len[n] = dfn[u]-dfn[v]+1;
            fa[n] = v;
            while (temp!=v){
                inCircle[temp] = true;
                addEdgeT(n,temp,min(dfn[temp]-dfn[v],len[n]-dfn[temp]+dfn[v]));
                temp = fa[temp];
            }
        }
    }
    if (!inCircle[u]){
        addEdgeT(fa[u],u,1);
    }
    dfs_clock--;
}
inline void update(int x,int w){
    if (w>=dp[x][0]){
        dp[x][1] = dp[x][0];
        dp[x][0]= w;
    }else if (w>dp[x][1]){
        dp[x][1] = w;
    }
}
void work(int squareU){
    head = 1;tail = 0;
    int length = len[squareU];
    for (int i=0;iint v = ET[squareU][i];
        while (head<=tail&&head1-length/2)head++;
        if (head<=tail&&head>=i+1-length/2){
            ans = max(ans,dp[v][0]+i+1+Q[head]);
        }
        while (head<=tail&&Q[tail]0]-i-1)tail--;
        Q[++tail] = dp[v][0]-(i+1);
    }
    for (int i=0;i2;i++){
        Max[i+1] = max(Max[i],dp[ET[squareU][i]][0]+i+1);
    }
    for (int i=length/2;i0]+length-i-1+Max[length/2-(length-i-1)]);
    }
}
void dfs(int u){
    for (int i=0;i0]+LenT[u][i]);
    }
    if (u>N){
        work(u);
    }else{
        ans = max(ans,dp[u][0]+dp[u][1]);
    }
}
int main(){
    input();
    tarjan(1);
    dfs(1);
    cout<return 0;
}

你可能感兴趣的:(仙人掌,BZOJ,POJ,洛谷)