首先对于给出的图建立圆方树,然后我们分类讨论每一个点作为中间的中转站出现的情况有多少种,累积到 \(ans\) 中。

  对于圆点:在任意两个子树内分别选出一个节点都是合法的。

  对于方点:连接向方点的点均为处于一个双联通分量中的点,彼此之间两两可。所以若我们让这个双联通分量上的一个点作为中转站,在其他任意的两棵子树内挑出两个点来都是合法的。这样乍一看好像是 \(n^{2}\) 的统计方法,我们不妨改变一下:因为答案是累加起来的,我们分别考虑每一棵子树对于答案造成的贡献。这一棵子树中的点可以和另一棵子树内的点任意匹配选出两个,对于不在这两棵子树内的双联通分量上的点均产生有贡献。然后就可以愉快的 \(O(n)\) 统计啦~

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 500000
int N, n, m, tot, S[maxn], fa[maxn];
int timer, dfn[maxn], low[maxn];
int ans, size[maxn], cnt[maxn];
bool vis[maxn]; struct edge
{
int cnp = , head[maxn], to[maxn], last[maxn];
void add(int u, int v)
{
if(u == v) return;
to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++;
to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++;
}
}E1, E2, E3; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void Tarjan(int u)
{
dfn[u] = low[u] = ++ timer; S[++ S[]] = u;
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i];
if(!dfn[v])
{
Tarjan(v); low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u])
{
E2.add(++ N, u); cnt[N] ++, cnt[u] ++; int x = ;
do
{
E2.add(N, x = S[S[] --]); cnt[N] ++, cnt[x] ++;
}while(x != v);
}
}
else low[u] = min(low[u], dfn[v]);
}
} void dfs(int u)
{
vis[u] = ;
if(u <= n) size[u] ++;
for(int i = E2.head[u]; i; i = E2.last[i])
{
int v = E2.to[i]; if(v == fa[u]) continue;
fa[v] = u; dfs(v); size[u] += size[v];
}
} void DP(int u)
{
for(int i = E2.head[u]; i; i = E2.last[i])
{
int v = E2.to[i]; if(v == fa[u]) continue;
DP(v);
}
int tem = ;
if(u <= n)
{
for(int i = E2.head[u]; i; i = E2.last[i])
{
int v = E2.to[i];
if(v != fa[u]) tem += (size[v]) * (tot - size[v] - );
else tem += (tot - size[u]) * (size[u] - );
}
}
else
{
if(cnt[u] > )
{
for(int i = E2.head[u]; i; i = E2.last[i])
{
int v = E2.to[i];
if(v != fa[u]) tem += (size[v]) * (tot - size[v]) * (cnt[u] - );
else tem += (tot - size[u]) * (size[u]) * (cnt[u] - );
}
}
}
ans += tem;
} signed main()
{
N = n = read(), m = read();
for(int i = ; i <= m; i ++)
{
int u = read(), v = read();
E1.add(u, v);
}
for(int i = ; i <= n; i ++)
if(!dfn[i]) Tarjan(i);
for(int i = ; i <= N; i ++)
if(!vis[i])
{
dfs(i); tot = size[i];
DP(i);
}
printf("%lld\n", ans);
return ;
}

最新文章

  1. Oracle 列转行函数 Listagg()
  2. BZOJ 1588: [HNOI2002]营业额统计
  3. Matlab 矩阵卷积理解(转载)
  4. android中ListView_SimpleAdapter
  5. HDU 1028 Ignatius and the Princess III (递归,dp)
  6. cocos2d-x 用浏览器打开网页
  7. C#中静态方法和非静态方法的区别(二)
  8. Bootstrap之Button.js
  9. ZOJ 3829 Known Notation 乱搞
  10. (2)java程序走一遍工作流activiti
  11. linux编译安装时常见错误解决办法
  12. 华为模拟器eNSP安装(最新)网络工程师必备!
  13. Kivy中文编程指南--https://cycleuser.gitbooks.io/kivy-guide-chinese/content/
  14. springMVC源码学习之获取参数名
  15. JAVAWEB 一一框架整合(SSI : Spring+SpringMVC+ ibtis)
  16. 【LeetCode】200. 岛屿的个数
  17. 前端学习 -- Css -- 内联元素的盒模型
  18. as3 关闭加载流
  19. trmd_b1_ok
  20. WPF字体模糊解决方案

热门文章

  1. P2664 树上游戏
  2. SAX-xml解析
  3. Ruby 基础教程 1-1
  4. python3 爬虫爬取深圳公租房轮候库(深圳房网)
  5. Siki_Unity_1-3_Unity零基础入门_古迹探险
  6. java后台接受web前台传递的数组参数
  7. Oracle作业练习题
  8. JAVA 面试须知
  9. 【shell 每日一练6】初始化安装Mysql并修改密码
  10. codeforces 301D Yaroslav and Divisors(树状数组)