原题链接
总共有 n n n 个节点, m m m 条路径,要求其中 m − 2 m-2 m−2 条路径走两遍,剩下 2 2 2 条路径仅走一遍,问不同的路径总数有多少,如果仅走一遍的两条边不同则将这两条路径视为不同。
1 ≤ n , m ≤ 1 × 1 0 6 1\le n,m\le 1\times10^6 1≤n,m≤1×106
5 4
1 2
1 3
1 4
1 5
6
考虑直接将每条边都当作两条边,那么题意及从 2 n 2n 2n 条边任选 2 2 2 条不是连接相同节点的边删除后图存在欧拉路,求删边的方案数。
如果本来的图不连通,那么肯定没有合适的方案,直接输出 0 0 0 就可以了。
否则我们在分析:由于我们是将每条边当作两条边的,那么对于建的图来说,每个顶点的度数为偶数,及一定存在欧拉路。
那么就来考虑删的边了。
我们可以把边分为自环和非自环两类。
为什么呢?
因为自环只跟一个顶点有关系,而非自环跟两个顶点有关系。
如果删除了一个自环,那剩下的所有点的度数仍然为偶数,所以可以随意删除下一条边。
而对于删除的第二条边,如果是非自环,那么图就有两个积点,存在欧拉路径,如果是自环,那么图所有点为偶点,存在欧拉回路。
所以可以得出自环的贡献为:
c n t × ( m − c n t ) + c n t × ( c n t − 1 ) / 2 cnt \times (m-cnt)+cnt\times (cnt-1)/2 cnt×(m−cnt)+cnt×(cnt−1)/2
其中 c n t cnt cnt 为自环数, m m m 是边数。
如果删除两条非自环,如果可行,那么它们一定共顶点,只有这样才会使共顶点的度数 − 2 -2 −2,仍为偶点,两外两个点的度数 − 1 -1 −1,为积点,存在欧拉路径。
所以非自环的贡献为:
∑ i = 1 n l i × ( l i − 1 ) 2 \sum_{i=1}^{n}\frac{l_i\times(l_i-1)}{2} ∑i=1n2li×(li−1)
其中 l i l_i li 为每个顶点连接的边数(不包括自环)。
#include
#define int long long
const int N=1e6+1;
using namespace std;
vector<int>E[N];
int n,m,x[N],y[N],deg[N],ans,vis[N],cnt,fa[N];
inline int ga(int x)
{
return x==fa[x]?fa[x]:fa[x]=ga(fa[x]);
}
inline void uni(int x,int y)
{
int fx=ga(x),fy=ga(y);
if(fx!=fy)
fa[fx]=fy;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
cin>>n>>m;
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
cin>>x[i]>>y[i];
uni(x[i],y[i]);
if(x[i]==y[i])
cnt++;
else
E[x[i]].push_back(y[i]),E[y[i]].push_back(x[i]);
}
for(int i=2;i<=m;i++)
if(ga(x[i])!=ga(x[1]))
cout<<"0",exit(0);
for(int i=1;i<=n;i++)
{
int le=E[i].size();
ans+=le*(le-1)/2;
}
ans+=cnt*(cnt-1)/2;
ans+=cnt*(m-cnt);
cout<<ans;
return 0;
}
更多方法