地址:https://loj.ac/problem/2587
先来说说我和这道题的故事吧。
作为APIO2018最良心得分题,放在T3的位置是真的阴……
当打完了T1,T2的暴力后,才发现T3貌似很好拿分,想的是把T1,T2的二档暴力打完后来慢慢拿T3的分。于是就被坑进T1了。
T3最后只打了有环和链的、n<=10的。树的档都没看到,血亏。
知道了点双树(圆方树)后,这道题就很送分了。对于圆点统计从不同子树中选出两个点的方案,对于方点统计从不同子树选出三个点的方案,加起来就行了。
细节比较多,详见注释
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<stack>
#include<map>
#define LL long long
using namespace std;
const int maxm = 4e5 + 5;
const int maxn = 2e5 + 5;
typedef pair<int, int > pii;
map<pii, int > mp;
struct edge {
int v, next;
} e[maxm << 2];
int head[maxn], cnt;
int n, m, u, v;
void adde(const int &u, const int &v) {
e[cnt] = (edge) {v, head[u]};
head[u] = cnt++;
}
stack<int > stk;
int dfn[maxn], low[maxn], vis[maxm], E_blk[maxm], bcnt, dcnt;
int ndcnt, is_sq[maxn];
int tarjan(int u) {
dfn[u] = low[u] = ++dcnt;
for(register int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(vis[i >> 1]) continue;
//点双树标记边 ,压边的栈
vis[i >> 1] = 1, stk.push(i);
if(dfn[v]) {
//点双的low不具有传递性,因为下面的子树会被点u挡住,只能用dfn[v]
low[u] = min(dfn[v], low[u]);
continue;
}
low[u] = min(low[u], tarjan(v));
if(low[v] >= dfn[u]) {
int id = stk.top();
//特判不在点双中的边
if(e[id ^ 1].v == u) {
stk.pop();
continue;
}
//在点双中才分配标号
++bcnt;
do {
id = stk.top(), stk.pop();
E_blk[id >> 1] = bcnt;
} while(e[id ^ 1].v != u);
}
}
return low[u];
}
namespace CST {
int head[maxn << 1], siz[maxn << 1];
void adde(const int &u, const int &v) {
//cerr << u << "---->" << v << endl;
e[cnt] = (edge) {v, head[u]};
head[u] = cnt++;
}
int dfs(int u, int p) {
siz[u] = u <= n;
for(register int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(v == p) continue;
siz[u] += dfs(v, u);
}
return siz[u];
}
LL GetAns(int u, int tot, int p) {
LL ans = 0, sum = 0, tms = 0;
//下面就是简单的讨论一下
siz[u] = u <= n;
for(register int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(v == p) continue;
ans += GetAns(v, tot, u);
siz[u] += siz[v];
tms += 1ll * sum * siz[v], sum += siz[v];
}
tms += (tot - siz[u]) * sum;
sum += tot - siz[u];
tms *= 2;
if(u <= n) {
ans += tms;
} else {
//ans += tms;
for(register int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(v == p) continue;
ans += 1ll * (tms - 2 * siz[v] * (sum - siz[v]));
}
ans += 1ll * (tms - 2 * (tot - siz[u]) * (sum - (tot - siz[u])));
}
//printf("%d %d\n", u, ans);
return ans;
}
}
int main() {
memset(head, -1, sizeof(head));
memset(CST::head, -1, sizeof(CST::head));
scanf("%d%d", &n, &m);
for(register int i = 1; i <= m; ++i)
scanf("%d%d", &u, &v), adde(u, v), adde(v, u);
for(register int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(i);
for(register int i = 0; i < m; ++i) {
u = e[i << 1].v, v = e[i << 1 | 1].v;
if(!E_blk[i]) {
if(u > v) swap(u, v);
if(mp[make_pair(u, v)]) continue;
CST::adde(u, v), CST::adde(v, u);
mp[make_pair(u, v)] = 1;
} else {
int S = E_blk[i] + n;
if(!mp[make_pair(u, S)]) {
CST::adde(u, S), CST::adde(S, u);
mp[make_pair(u, S)] = 1;
}
if(!mp[make_pair(v, S)]) {
CST::adde(v, S), CST::adde(S, v);
mp[make_pair(v, S)] = 1;
}
}
}
LL ans = 0;
for(register int i = 1; i <= n; ++i)
if(!CST::siz[i]) {
CST::dfs(i, 0);
ans += CST::GetAns(i, CST::siz[i], 0);
}
printf("%lld\n", ans);
return 0;
}
rockdu
没有帐号? 立即注册