#P1222. *【仙人掌】无向连通图的支撑子图个数[SHOI2006]仙人掌
*【仙人掌】无向连通图的支撑子图个数[SHOI2006]仙人掌
Description
【题目描述】给出一个有 $n$ 个点 $m$ 条路径的无向连通图,判读其是否仙人掌图,且求出其支撑子图的个数。

支撑子图(spanning subgraph):支撑子图也是原图的子图,这种子图可以比原来少一些边,但是不能破坏图的连通性,也不能去除原来图上的任何顶点。“支撑”的概念类似于我们熟知的“最小支撑树”,对于上例中的第一张图来说,任意去除回路I中的图或回路II中的一条边都能构成一个支撑子图,所以它的支撑子图一共有6 + 4 + 6 × 4 + 1 = 35种(注意图自身也是自己的一个子图)
【输入格式】
第一行两个整数 $n \ m$($1 \le n \le 2×10^4$,$0 \le m \le 10^3$)。
下来 $m$ 行,每行表示一条路径。
每行:开始有一个整数 $k$($2 \le k \le 10^3$),代表在这条路径上的顶点个数。下来是 $k$ 个 $1$ 到 $n$ 之间的整数,分别对应了一个顶点,相邻的顶点表示存在一条无向边。
【输出格式】
输出无向连通图的支撑子图的个数,如果它不是一张仙人掌图,输出0。注意最后的答案可能是一个很大很大的数。
【样例输入1】
14 3
9 1 2 3 4 5 6 7 8 3
7 2 9 10 11 12 13 10
2 2 14
【样例输出1】
35
【样例输入2】
10 2
7 1 2 3 4 5 6 1
6 3 7 8 9 10 2
【样例输出2】
0
【样例输入3】
5 1
4 1 2 3 4
【样例输出3】
0
Hint
scy不能过样例的代码:
#include<bits/stdc++.h> using namespace std; const int N=2e4+10;</p>struct node { int a[2100], len; node(){memset(a,0,sizeof(a));len=1;} }ans; node operator (node no,int x) { for(int i=1;i<=no.len;i++)no.a[i]=x; for(int i=1;i<=no.len;i++){no.a[i+1]+=no.a[i]/10000;no.a[i]%=10000;} int i=no.len; while(no.a[i+1]>0) { no.a[i+1]+=no.a[i]/10000; no.a[i]%=10000; i++; } no.len=i; return no; }
vector<int>G[N]; int tsp,dfn[N],low[N],dep[N]; bool flag;
void tarjan(int x,int xfa) { dfn[x]=low[x]=++tsp; int num=0; for(int y:G[x])if(y!=xfa) { if(!dfn[y]) { dep[y]=dep[x]+1; tarjan(y,x); low[x]=min(low[x],low[y]); } else low[x]=min(low[x],dfn[y]); if(dfn[x]>dfn[y]) { num++; ans=ans*(dep[x]-dep[y]+2); } } if(num>1)flag=0; } int main() { int n,m;scanf("%d%d",&n,&m); while(m--) { int k,x,y;scanf("%d%d",&k,&x);k--; while(k--) { scanf("%d",&y); G[x].push_back(y);G[y].push_back(x); x=y; } } tsp=0;memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low)); memset(dep,0,sizeof(dep)); ans.a[1]=1;flag=1;tarjan(1,0); if(tsp!=n)flag=0; if(!flag)printf("0"); else { printf("%d",ans.a[ans.len]); for(int i=ans.len-1;i>=1;i--)printf("%04d",ans.a[i]); } printf("\n"); return 0; }
#include<bits/stdc++.h> using namespace std;</p>const int N = 2e4 + 10; const int P = 10000;
struct hipr { int len; int a[3010]; hipr() { memset(a, 0, sizeof(a)); len = 1; } } ans;
hipr operator*(hipr hp, int x) { hipr res; res.len = hp.len;
for (int i = 1; i <= res.len; i++) { res.a[i] = hp.a[i] * x; } for (int i = 1; i <= res.len; i++) { res.a[i + 1] += res.a[i] / P; res.a[i] %= P; } int i = res.len; while (res.a[i + 1] > 0) { i ++; res.a[i + 1] += res.a[i] / P; res.a[i] %= P; } res.len = i; return res;}
int k[N]; vector<int> G[N]; int tsp, dfn[N], low[N], dep[N]; bool flag;
void tarjan(int x, int xfa) { dfn[x] = low[x] = ++tsp; dep[x] = dep[xfa] + 1; int sum = 0; for (int y : G[x]) if (y != xfa) { if (!dfn[y]) { tarjan(y, x); low[x] = min(low[x], low[y]);
if (low[y] < dfn[x]) { //区别在这里,要让环上的每个点都知道有环 // 可以自己模拟样例 2看看 sum ++; } } else { low[x] = min(low[x], dfn[y]); } if (dfn[x] > dfn[y]) { sum ++; ans = ans * (dep[x] - dep[y] + 2); } } if (sum > 1) { flag = 0; }}
int main () { ios::sync_with_stdio(False); cin.tie(0);
int n, m; cin >> n >> m; for (int i = 1; i <= m; i++) { cin >> k[i]; int pre; cin >> pre; for (int j = 2; j <= k[i]; j++) { int x; cin >> x; G[pre].push_back(x); G[x].push_back(pre); pre = x; } } tsp = 0; memset (dfn, 0, sizeof(dfn)); memset (low, 0, sizeof(low)); ans.len = 1; ans.a[1] = 1; dep[0] = 0; flag = 1; tarjan(1, 0); if (tsp != n) { flag = 0; } if (!flag) { cout << '0' << "\n"; } else { cout << ans.a[ans.len]; for (int i = ans.len - 1; i >= 1; i--) { cout << setw(4) << setfill('0') << ans.a[i]; } cout << "\n"; } return 0;}