HZOJ 星际旅行
正解欧拉路,其实看完题解还是挺简单的,由于对欧拉路这种东西没怎么接触过,所以考试时没想出来,知识还是有漏洞啊。
另外这题的题解写的也不是很清楚(可能大佬作者觉得这是一道送分题……),首先判断联通(注意是边联通,即使是有一个点孤立也不会影响方案数),可以用dfs或并查集,注意dfs有一个坑点,不要直接就从1开始搜,因为1可能是那个被孤立的点。然后就可以求方案数了:
将无向图拆成双向边,显然每个点的度为偶数,是欧拉路,那么问题就转化成了从中去掉两条边使其仍然是欧拉路,题目中特别提醒了自环,所以分三种情况:
1.去掉两个自环,显然还是欧拉路,ans+=(num)*(num-1)/2;然而我一开始忘了除二(之前犯过的错误,该打)。
2.去掉一个自环和一条普通边,自环显然没问题,但是去掉普通边之后仍然是欧拉路吗?可以从去掉的那条边的反边起点开始走过去,那么每个点的度数仍然为偶数,仍然为欧拉路,ans+=num*(m-num);
3.剩下去掉普通边的情况了,怎么去才合法呢?去掉一个点的一条入边和一条出边,那么显然仍然是欧拉路,证明同上。去掉其他边呢?并不满足题意。所以ans+=$C_{du_i}^{2}$。
#include<iostream> #include<cstdio> #define MAXN 100100 #define int LL #define LL long long #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; struct edge { int u,v,nxt; #define u(x) ed[x].u #define v(x) ed[x].v #define n(x) ed[x].nxt }ed[MAXN*4]; int first[MAXN],num_e; #define f(x) first[x] int n,m; LL C[500010][5]; void get_C() { C[0][0]=1; for(int i=1;i<=500000;i++) { C[i][0]=1; for(int j=1;j<=min(i,4);j++) C[i][j]=C[i-1][j]+C[i-1][j-1]; } } bool v[MAXN],ve[MAXN*4]; void pd(int x,int fa) { v[x]=1; for(int i=f(x);i;i=n(i)) { ve[i]=1; if(v(i)!=fa&&!v[v(i)])pd(v(i),x); } } int num,ans; int du[MAXN]; inline int read(); inline void add(int u,int v); signed main() { get_C(); n=read(),m=read();int a,b; for(int i=1;i<=m;i++) { a=read(),b=read(); add(a,b);add(b,a); if(a==b)num++; else du[a]++,du[b]++; } for(int i=1;i<=n;i++)if(du[i]){pd(i,0);break;} for(int i=1;i<=num_e;i++)if(!ve[i]){puts("0");return 0;} ans+=num*(num-1)/2; ans+=num*(m-num); for(int i=1;i<=n;i++) ans+=C[du[i]][2]; cout<<ans<<endl; } inline void add(int u,int v) { ++num_e; u(num_e)=u; v(num_e)=v; n(num_e)=f(u); f(u)=num_e; } inline int read() { int s=0;char a=getchar(); while(a<'0'||a>'9')a=getchar(); while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();} return s; }
波澜前,面不惊。