BZOJ.1023.[SHOI2008] cactus cactus map (DP)

topic link

Similar to finding the diameter of a tree, you can use a (similar) tree-shaped DP to find the longest chain and sub-longest chain of each point's subtree (in a cactus, it is an induced subgraph), and use the max{maximum of the different subtrees of each point's sub-nodes. long chain}+max{second long chain} Update answer. (There is no need to store the second-longest chain. During the solution process, update ans first, and then update the longest chain.)
Let f[i] be the length of the longest chain in the induced subgraph of point i.
For the ring, we find a point x with the smallest dep[] on the ring to represent the ring as a point (dep is updated in the order of DFS), and find f[x], the part outside the ring can be done directly like a tree .
For ring processing: f[x] is more obvious, f[x]=max{f[v]+dis(x,v)}, v is a point on the ring, dis(x,v) is x, v is in Minimum distance on the ring.
How does the ring update the answer? Take out all the points on the ring, ans=max{f[u]+f[v]+dis(u,v)}.
u, v are points on the ring, numbered in sequence, dis(u,v)=vu(vu<=len/2), then ans can be written as max{f[u]-u+f[v]+v} . Fix v, because u is monotonically increasing, so the largest f[u]-u before is also the largest in the back, which can be maintained by a monotonic queue.
dis() is the minimum distance on the ring, so vu cannot exceed ring length/2. Because it is a ring, split it into a sequence of 3/2*len to update ans.
Then update f[x] with f[v]+dis(x,v).
Sweep all the rings, the total complexity is \(O(m)\) .

Total: Tarjan, for points not on the same ring, update ans with f[x]+f[v]+1, then update f[x] with f[v]; for other points, take out the ring monotonic as above Queue processing.

//8760kb    192ms
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
const int N=5e4+5,M=N<<2;//边数。。大概最多2n条?

int n,m,Ans,Enum,H[N],to[M],nxt[M],dfn[N],low[N],id,fa[N],dep[N],f[N],q[N],A[N<<1];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
inline void AddEdge(int u,int v){
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DP(int x,int ed)
{
    int n=dep[ed]-dep[x]+1;//length
    for(int i=n; i; --i) A[i]=f[ed], ed=fa[ed];
    int n2=n+(n>>1);//能同时更新别的的最多只有n/2个点,所以只需要3/2*n 
    for(int i=n+1; i<=n2; ++i) A[i]=A[i-n];
    int h=1,t=1; q[1]=1;
    for(int i=2; i<=n2; ++i)//i,q[]是点,拿出来的A[]是f[]!
    {
        while(h<t && i-q[h]>(n>>1)) ++h;
        Ans=std::max(Ans,A[q[h]]-q[h]+A[i]+i);
        while(h<=t && A[q[t]]-q[t]<=A[i]-i) --t;//注意这个比较是<,因为更新队首时不是根据值的大小,而是限制条件(<=竟然有90...) 
        q[++t]=i;
    }
    for(int i=2; i<=n; ++i)
        f[x]=std::max(f[x],A[i]+std::min(i-1,n-i+1));
}
void DFS(int x)
{
    dfn[x]=low[x]=++id;
    for(int v,i=H[x]; i; i=nxt[i])
        if((v=to[i])!=fa[x])
        {
            if(!dfn[v])
                fa[v]=x, dep[v]=dep[x]+1, DFS(v), low[x]=std::min(low[x],low[v]);
            if(low[v]>dfn[x])//不是环 
                Ans=std::max(Ans,f[x]+f[v]+1), f[x]=std::max(f[x],f[v]+1);
            low[x]=std::min(low[x],dfn[v]);//无向图,就不需要什么在栈中了 //只需判环,所以和下一行写法一样 
//          low[x]=std::min(low[x],low[v]);
        }
    for(int i=H[x]; i; i=nxt[i])
        if(fa[to[i]]!=x&&dfn[to[i]]>dfn[x]) DP(x,to[i]);//找环的另一个端点 
        //端点是后访问的点,而不只是&&to[i]!=fa[x]!(当x同时在两个环上时)能找到之前的环和x之后的环,但x不代表(没必要)之前访问的环,这样找环还麻烦。。不是沿to[i]的fa到x。
}

int main()
{
    n=read(),m=read();
    int num,u,v;
    while(m--){
        num=read()-1, u=read();
        while(num--) v=read(),AddEdge(u,v),u=v;
    }
    DFS(1);
    printf("%d",Ans);

    return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325082488&siteId=291194637