黑骑士
缩点·树形Dp
题目大意:
给你一个图,保证每个点最多属于一个简单环,每个点度数最多为3,求这个图的“眼镜图形个数”
保证图是联通的
题解:
先找环缩点,然后f[i]表示i的子树中有多少个 一条路径+一个圈,分当前点是不是缩起来的环 两种情况,统计答案即可。详见代码。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
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);
}