SHOI2006仙人掌

Posted hansue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SHOI2006仙人掌相关的知识,希望对你有一定的参考价值。

题目链接

 

分析:

  无向仙人掌比较简单。

  一个仙人掌首先是个连通图。一遍深搜可以筛掉。

  根据定义,如果有任意边被不同的简单环经过,它不是仙人掌。

  那么我们现在要对边,计数经过它的环。

  这里的做法是树上差分。第一遍深搜还要搞出$DFS$树和倍增数组,遇到非树边记录下来。

  $Trickquad vis$数组设三个状态:出边到达$0$点,这条边作为树边,到达的点改成$1$点;到达$1$点,记录成环,自己变成$2$点;到达$2$点,这条成环边已经被记录过,不用管。

  用每个点表示它的“到父边”上的计数,就是在每条成环边的端点上$++$,它们的$LCA$上$-=2$,最后再来一遍深搜或者借助$DFN$序由下往上传标记即可。

  这样得到的边上的计数不代表实际有多少个简单环经过它。但一旦出现共边的简单环,一定有边上的计数超过$1$。这就够了。

  接下来计算“仙人数”。

  既然不能破坏连通性,那么不能剪那些不在简单环上的树边。发现去掉任意简单环上的一条边满足条件。于是答案就很显然了。设有$k$个简单环,第$i$个简单环有$s_i$条边,那么$$ans=prodlimits_{i=1}^{k}(s_i+1)$$

  用$LCA$算$s$值,加上高精度,没了。

 

实现(100分):

技术图片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define IL inline
#define RG register
#define _1 first
#define _2 second
using namespace std;
typedef long long LL;
const int N=2e4;
const int M=1e6;
const int L=15;

struct Edge{
    int to,nxt;
    Edge(){}
    Edge(int p1,int p2):to(p1),nxt(p2){}
}e[M*2+3];
    int etop,h[N+3];
    
IL void adde(int u,int v){
    e[++etop]=Edge(v,h[u]);    h[u]=etop;
    e[++etop]=Edge(u,h[v]);    h[v]=etop;
    
}

    int n,m;
    int dep[N+3],fa[N+3][L+3];
    int cnt,mdep,mlog;
    int vis[N+3];
    int k,v1[M+3],v2[M+3];

void dfs(int u,int fr){
    for(int i=h[u];~i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fr)
            continue;
        if(vis[v]==1){
            k++;
            v1[k]=u;
            v2[k]=v;
            vis[u]=2;
            
        }
        if(vis[v]>0)
            continue;
            
        vis[v]=1;
        dep[v]=dep[u]+1;
        mdep=max(mdep,dep[v]);
        fa[v][0]=u;
        cnt++;
        dfs(v,u);
        
    }
    
}

IL void swap(int &x,int &y){
    int t=x;    x=y;    y=t;
}

IL int lca(int x,int y){
    if(dep[x]<dep[y])
        swap(x,y);
    
    for(int l=mlog;l>=0;l--)
    if(dep[fa[x][l]]>=dep[y])
        x=fa[x][l];
        
    if(x==y)
        return x;
    
    for(int l=mlog;l>=0;l--)
    if(fa[x][l]!=fa[y][l]){
        x=fa[x][l];    y=fa[y][l];
    }
    
    return fa[x][0];
    
}

    int mrk[N+3];
    
void dfs2(int u){
    for(int i=h[u];~i;i=e[i].nxt){
        int v=e[i].to;
        if(vis[v])
            continue;
            
        vis[v]=true;
        dfs2(v);
        mrk[u]+=mrk[v];
        
    }
    
}

const int Len=3180;
const LL wgt=1e8;

struct Num{
    int l;
    LL a[Len+3];
    
    Num(){
        l=1;    memset(a,0,sizeof a);
    }
    Num(LL x){
        l=0;    memset(a,0,sizeof a);
        while(x>0){
            a[++l]=x%wgt;
            x/=wgt;
            
        }
        
    }
    
    IL LL operator[](int i){
        return a[i];
    }
    
};

IL Num operator+(Num x,Num y){
    Num z;    z.l=max(x.l,y.l);
    for(int i=1;i<=z.l;i++){
        z.a[i]+=x[i]+y[i];
        z.a[i+1]+=z[i]/wgt;
        z.a[i]%=wgt;
        
    }
    if(z[z.l+1]>0)
        z.l++;
    return z;
    
}

IL Num operator*(Num x,Num y){
    Num z;    z.l=x.l+y.l+2;
    for(int i=1;i<=x.l;i++)
        for(int j=1;j<=y.l;j++)
            z.a[i+j-1]+=x[i]*y[j];
    for(int i=1;i<=x.l;i++)
    if(z[i]>=wgt){
        z.a[i+1]+=z[i]/wgt;
        z.a[i]%=wgt;
        
    }
    while(z[z.l]==0)
        z.l--;
    return z;
    
}

IL Num operator*=(Num &x,Num y){
    return x=x*y;
}

IL void write(Num x){
    printf("%lld",x[x.l]);
    for(int i=x.l-1;i>=1;i--)
        printf("%08lld",x[i]);
}

int main(){
    scanf("%d%d",&n,&m);
    etop=-1;
    memset(h,-1,sizeof h);
    for(int i=0;i<m;i++){
        int k,p,v;    scanf("%d%d",&k,&p);
        for(int j=1;j<k;j++){
            scanf("%d",&v);
            adde(p,v);
            p=v;
            
        }
        
    }
    
    dep[1]=1;    fa[1][0]=fa[0][0]=dep[0]=0;    
    mdep=0;
    memset(vis,0,sizeof vis);
    vis[1]=true;    cnt=1;
    dfs(1,0);
    
    if(cnt<n){
        printf("0");
        return 0;
        
    }
    
    for(mlog=0;mdep>0;mdep>>=1)
        mlog++;
    for(int l=1;l<=mlog;l++)
        for(int i=1;i<=n;i++)
            fa[i][l]=fa[fa[i][l-1]][l-1];
    
    memset(mrk,0,sizeof mrk);
    for(int i=1;i<=k;i++){
        int x=v1[i],y=v2[i];
        int z=lca(x,y);
        
        if(x==z){
            mrk[y]++;    mrk[x]--;
        }
        else 
        if(y==z){
            mrk[x]++;    mrk[y]--;
        }
        else {
            mrk[x]++;    mrk[y]++;
            mrk[z]-=2;
        }
        
    }
    
    memset(vis,0,sizeof vis);
    vis[1]=true;
    dfs2(1);
    
    for(int i=1;i<=n;i++)
    if(mrk[i]>1){
        printf("0");
        return 0;
        
    }
    
    Num ans(1);
    for(int i=1;i<=k;i++){
        int x=v1[i],y=v2[i];
        int z=lca(x,y);
        
        
        if(x==z)
            ans*=Num(1LL*dep[y]-dep[z]+2);
        else 
        if(y==z)
            ans*=Num(1LL*dep[x]-dep[z]+2);
        else 
            ans*=Num(1LL*dep[x]+dep[y]-2*dep[z]+2);
        
    }
    
    write(ans);

    return 0;

}
View Code

 

小结:

  记得讨论范围,没开$long;long$和没上高精然后$WA$掉一样难受。

  树上边计数:树上差分。

以上是关于SHOI2006仙人掌的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1023 [SHOI2008]cactus仙人掌图 & poj3567 Cactus Reloaded——求仙人掌直径

bzoj 1023[SHOI2008]cactus仙人掌图

P4244 [SHOI2008]仙人掌图 II

bzoj1023: [SHOI2008]cactus仙人掌图

[SHOI2008]cactus仙人掌图

BZOJ 1023SHOI 2008cactus仙人掌图