黑骑士

黑骑士

缩点·树形Dp

题目大意:

给你一个图,保证每个点最多属于一个简单环,每个点度数最多为3,求这个图的“眼镜图形个数”
保证图是联通的

题解:

先找环缩点,然后f[i]表示i的子树中有多少个 一条路径+一个圈,分当前点是不是缩起来的环 两种情况,统计答案即可。详见代码。

Code:

#include 
#include 
#include 
#define D(x) cout<<#x<<" = "<
#define E cout<
using namespace std;
typedef long long ll;
const int N = 2000005;
const ll mod = 19260817;

void read(int &num){
    num=0; char c;
    while(!isdigit(c=getchar()));
    num=c-'0';
    while(isdigit(c=getchar())) num=num*10+c-'0';
}

int n,m,s[N],top,in[N],circle,belong[N]; 
bool vis[N]; ll ans;

struct Edge{ int from,to,nxt; }e[N<<1],tp[N<<1]; 
int head[N],ec=1;
void add(int a,int b){ 
    e[++ec].to=b; e[ec].from=a;
    e[ec].nxt=head[a]; head[a]=ec; 
}

void dfs(int u,int from){
    if(in[u]){
        circle++;
        for(int i=in[u];i<=top;i++)
            belong[s[i]]=circle;
        return;
    }
    s[++top]=u; in[u]=top;
    for(int i=head[u];i;i=e[i].nxt){
        if((i^1)==from) continue;
        if(vis[e[i].to]) continue;
        dfs(e[i].to,i); 
    }
    top--; in[u]=0; vis[u]=true;
}

void rebuild(){
    for(int i=2;i<=ec;i++) tp[i]=e[i];
    int tot=ec; memset(head,0,sizeof(head)); ec=1;
    for(int i=2;i<=tot;i++){
        int u=tp[i].from, v=tp[i].to;
        if(belong[u]!=belong[v]) 
            add(belong[u],belong[v]);
    }
}

ll dp(int u,int f){
    ll res=0,tot=0,cnt;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to; if(v==f)continue;
        cnt=dp(v,u); res=(res+tot*cnt%mod)%mod; tot+=cnt;
    }
    if(u>n){
        res=(res*2+tot)%mod;
        tot=tot*2+1;
    }
    ans=(ans+res)%mod;
    return tot;
}

int main(){
    freopen("3.in","r",stdin);
    read(n); read(m);
    int a,b;
    for(int i=1;i<=m;i++){ 
        read(a); read(b);
        add(a,b); add(b,a); 
    }
    circle=n; for(int i=1;i<=n;i++) belong[i]=i;
    dfs(1,0); 
    rebuild();
    dp(n+1,0); 
    printf("%lld\n",ans);
}

你可能感兴趣的:(缩点,树形dp)