- 题目连接:https://www.luogu.com.cn/problem/P3388
- 技巧:割点模板题
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
#define pii pair <int,int>
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
const int N = 2e5 + 5;
int n , m;
int head[N] , cnt;
struct node {
int t , next;
}edge[N << 1];
void add (int f, int t)
{
edge[cnt].t = t;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
int dfn[N] , low[N] , cut[N], times;
void tarjan (int u , int root)
{
dfn[u] = low[u] = ++ times;
int tot = 0;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if (!dfn[v])
{
tarjan (v, root);
low[u] = min (low[u] , low[v]);
if (low[v] >= dfn[u])
{
tot ++;
if (u != root || tot > 1)
cut[u] = 1;
}
}
else
low[u] = min (low[u], dfn[v]);//这里一定要dfn[v]
}
}
int main ()
{
mem (head, -1);
read (n , m);
for (int i = 1 ; i <= m ; i ++)
{
int u , v;
read (u , v);
add (u, v) , add (v, u);
}
for (int i = 1 ; i <= n ; i ++)
if (!dfn[i])
tarjan (i, i);
int ans = 0;
for (int i = 1 ; i <= n ; i ++)
if (cut[i])
ans ++;
cout << ans << endl;
for (int i = 1 ; i <= n ; i ++)
if (cut[i])
cout << i << ' ';
cout << endl;
}
- 题目连接:https://www.acwing.com/problem/content/399/
- 技巧: 常规套路,边双缩点形成树之后,利用LCA求树上两点间的距离即可
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
#define pii pair <int,int>
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
const int N = 2e5 + 5;
int n , m , Q;
int head[N] , cnt;
struct node {
int t , next;
}edge[N << 1];
void add (int f, int t)
{
edge[cnt].t = t;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
int bridge[N << 1];
int dfn[N << 1] ,low[N << 1], times;
void tarjan(int u, int id)
{
dfn[u] = low[u] = ++times;
for(int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if(!dfn[v])
{
tarjan(v,i);
low[u] = min (low[u] , low[v]);
if(low[v] > dfn[u])
bridge[i] = bridge[i^1] = 1;
}
else if(i != (id ^ 1)) // 如果有重边
low[u] = min(low[u], dfn[v]);
}
}
//处理ecc
int ecc[N << 1], type, deg[N << 1];
void dfs(int u, int block)
{
ecc[u] = block;
for(int i = head[u] ; i != - 1 ; i = edge[i].next)
{
if(!bridge[i])
{
int v = edge[i].t;
if(!ecc[v])
dfs(v,block);
}
}
}
vector <int> Edge[N];
int bz[N][30] , deep[N];
void dfs1(int rt, int dep)
{
deep[rt] = dep;
for (auto v : Edge[rt])
{
if(!deep[v])
{
bz[v][0] = rt;
dfs1(v,dep + 1);
}
}
}
void deal()
{
for (int i = 1 ; i <= 25 ; i ++)
for (int j = 1 ; j <= n ; j ++)
bz[j][i] = bz[bz[j][i-1]][i-1];
}
int LCA(int x,int y)
{
if(deep[x] < deep[y])
swap(x,y);
int t = deep[x] - deep[y];
for(int i = 0; (1<<i) <= t ; i ++)
if((1<<i)&t)x=bz[x][i];
for(int i=19 ; i >= 0 ; i--)
if(bz[x][i] ^ bz[y][i])
x=bz[x][i], y=bz[y][i];
if(x==y) return x;
return bz[x][0];
}
int main ()
{
mem (head, -1);
read (n , m);
for (int i = 1 ; i <= m ; i ++)
{
int u , v;
read (u , v);
add (u , v), add (v, u);
}
for (int i = 1 ; i <= n ; i ++)
if (!dfn[i])
tarjan (i, -1);
for (int i = 1 ; i <= n ; i ++)
if (!ecc[i])
dfs (i, ++ type);
for (int i = 0 ; i < cnt ; i += 2)
{
int u = edge[i].t , v = edge[i + 1].t;
if (ecc[u] != ecc[v])
Edge[ecc[u]].pb(ecc[v]) , Edge[ecc[v]].pb(ecc[u]);
}
dfs1 (1, 1);
deal ();
read (Q);
while (Q --)
{
int u , v;
read (u, v);
int lca = LCA (ecc[u], ecc[v]);
//cout << ecc[u] << ' ' << ecc[v] << endl;
write(deep[ecc[u]] + deep[ecc[v]] - 2 * deep[lca]), LF;
}
}
- 题目连接:https://www.acwing.com/problem/content/366/
- 技巧:一开始的想法是ecc缩点之后,树链剖分回答询问,但是似乎有更好的做法,采用并查集暴力修改即可
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
#define pii pair <int,int>
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
const int N = 2e5 + 5;
int n , m, Q;
vector <int> Edge[N];
int head[N] , cnt;
struct node {
int t , next;
}edge[N << 1];
void add (int f, int t)
{
edge[cnt].t = t;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
int bridge[N << 1], fa[N];
int vec[N << 1], deep[N << 1];
int dfn[N << 1] ,low[N << 1], times, ans;
void tarjan(int u, int id)
{
dfn[u] = low[u] = ++times;
for(int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if(!dfn[v])
{
deep[v] = deep[u] + 1;
fa[v] = u;
tarjan(v,i);
low[u] = min (low[u] , low[v]);
if(low[v] > dfn[u])
bridge[i] = bridge[i^1] = 1, vec[v] = 1, ans += 1;
}
else if(i != (id ^ 1)) // 如果有重边
low[u] = min(low[u], dfn[v]);
}
}
//处理ecc
int ecc[N << 1], type, deg[N << 1];
void dfs(int u, int block)
{
ecc[u] = block;
for(int i = head[u] ; i != - 1 ; i = edge[i].next)
{
if(!bridge[i])
{
int v = edge[i].t;
if(!ecc[v])
dfs(v,block);
}
}
}
void Init ()
{
for (int i = 1 ; i <= n ; i ++)
Edge[i].clear();
type = times = ans = cnt = 0;
mem (fa, 0);
mem (deep, 0);
mem (head, -1);
mem (ecc , 0);
mem (deg , 0);
mem (bridge, 0);
mem (dfn, 0);
mem (vec, 0);
}
void LCA (int u , int v)
{
int ver[N] , tot = 0;
if (deep[u] < deep[v])
swap (u, v);
while (deep[u] > deep[v])
{
if (vec[u])
vec[u] = 0, ans --;
ver[++ tot] = u;
u = fa[u];
}
while (u != v)
{
if (vec[u]) ans -- , vec[u] = 0;
if (vec[v]) ans -- , vec[v] = 0;
ver[++ tot] = u;
ver[++ tot] = v;
u = fa[u];
v = fa[v];
}
int lca = u;
for (int i = 1 ; i <= tot ; i ++)
fa[ver[i]] = lca;
return ;
}
int main ()
{
int t = 1;
while (scanf("%d %d",&n , &m) != EOF)
{
if (n == 0 && m == 0)
break;
Init();
for (int i = 1; i <= m; i++)
{
int u, v;
read(u, v);
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i, i);
for (int i = 1; i <= n; i++)
if (!ecc[i])
dfs(i, ++type);
for (int i = 0; i < cnt; i++)
{
int u = edge[i].t , v = edge[i + 1].t;
if (ecc[u] != ecc[v])
Edge[u].pb(v) , Edge[v].pb(u);
}
printf ("Case %d:\n", t ++);
read (Q);
for (int i = 1 ; i <= Q ; i ++)
{
int u , v;
read (u , v);
LCA (u , v);
write (ans), LF;
}
LF;
}
}
- 题目连接:https://www.acwing.com/problem/content/397/
- 技巧:ecc缩点之后,寻找度数为1的点的个数即可,其最终答案为**(叶子节点数+1)/2**
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
#define pii pair <int,int>
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
const int N = 2e5 + 5;
int n , m, head[N] , cnt;
struct node {
int t , next;
}edge[N << 1];
void add (int f, int t)
{
edge[cnt].t = t;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
int bridge[N << 1];
int dfn[N << 1] , low[N << 1] , times;
void tarjan (int u , int id)
{
dfn[u] = low[u] = ++times;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if (!dfn[v])
{
tarjan (v, i);
low[u] = min (low[u] , low[v]);
if (low[v] > dfn[u])
bridge[i] = bridge[i ^ 1] = 1;
}
else if (i != (id ^ 1))
low[u] = min (low[u] , dfn[v]);
}
}
int ecc[N << 1] , type, deg[N << 1];
void dfs (int u , int block)
{
ecc[u] = block;
for (int i = head[u] ; i != - 1; i = edge[i].next)
{
if (!bridge[i])
{
int v = edge[i].t;
if (!ecc[v])
dfs (v, block);
}
}
}
int main ()
{
mem (head, -1);
read (n , m);
for (int i = 1 ; i <= m ; i ++)
{
int u , v;
read (u, v);
add (u, v);
add (v, u);
}
for (int i = 1 ; i <= n ; i ++)
if (!dfn[i])
tarjan (i , i);
for (int i = 1 ; i <= n ; i ++)
if (!ecc[i])
dfs (i , ++ type);
for (int i = 0 ; i < cnt ; i += 2)
{
int u = edge[i].t , v = edge[i + 1].t;
if (ecc[u] != ecc[v])
deg[ecc[u]] ++ , deg[ecc[v]] ++;
}
int ans = 0;
for (int i = 1 ; i <= type; i ++)
if (deg[i] == 1)
ans ++;
write ((ans + 1) >> 1), LF;
}
- 题目连接:https://www.acwing.com/problem/content/400/
- 技巧:vcc缩点后,建立圆方树,在缩点的同时,处理边所属的点双,必经点的个数 == 两个方点距离/2
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
#define pii pair <int,int>
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
const int N = 200005;
int n , m , head[N] , cnt;
struct node {
int t, next;
}edge[N];
void add (int f, int t)
{
edge[cnt].t = t;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
vector <pair<int,int>> Edge[N];
stack <int> st, st_e;
int dfn[N], low[N], fa[N], fa_id[N], times, vcc[N];
int vis[N], type;
void tarjan (int u)
{
dfn[u] = low[u] = ++ times;
st.push (u), vis[u] = 1;
int v , id , x;
for (auto it : Edge[u])
{
v = it.fi , id = it.se;
if (!dfn[v])
{
fa[v] = u, fa_id[v] = id;
st_e.push (id);
tarjan (v);
low[u] = min (low[u] , low[v]);
if (dfn[u] <= low[v])
{
++type;
while (1)
{
x = st.top();
add (type, x);
add (x, type);
vis[x] = 0 , st.pop();
if (x == v) break;
}
add (type, u);
add (u, type);
while (1)
{
id = st_e.top() , st_e.pop();
vcc[id] = type;
if (id == fa_id[v]) break;
}
}
}
else if (v != fa[u])
{
low[u] = min (low[u], dfn[v]);
if (dfn[v] < dfn[u]) st_e.push(id);
}
}
}
int bz[N << 1][30] , deep[N << 1];
void dfs(int rt, int dep)
{
deep[rt] = dep;
for (int i = head[rt] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if(!deep[v])
{
bz[v][0] = rt;
dfs(v,dep + 1);
}
}
}
void deal()
{
for (int i = 1 ; i <= 25 ; i ++)
for (int j = 1 ; j <= 2 * n ; j ++)
bz[j][i] = bz[bz[j][i-1]][i-1];
}
int LCA(int x,int y)
{
if(deep[x] < deep[y])
swap(x,y);
int t = deep[x] - deep[y];
for(int i = 0; (1<<i) <= t ; i ++)
if((1<<i)&t)x=bz[x][i];
for(int i=19 ; i >= 0 ; i--)
if(bz[x][i] ^ bz[y][i])
x=bz[x][i], y=bz[y][i];
if(x==y) return x;
return bz[x][0];
}
void Init()
{
for (int i = 1 ; i <= n ; i ++)
Edge[i].clear();
cnt = 0;
type= n;
mem (vcc, 0);
mem (head,-1);
mem (dfn, 0);
mem (deep, 0);
mem (low, 0);
mem (bz, 0);
mem (fa, 0);
mem (fa_id, 0);
}
int main ()
{
while (scanf ("%d %d",&n, &m) != EOF)
{
if (n == 0 && m == 0)
break;
Init ();
for (int i = 1 ; i <= m ; i ++)
{
int u , v;
read (u, v);
Edge[u].pb(mk(v, i));
Edge[v].pb(mk(u, i));
}
for (int i = 1 ; i <= n ; i ++)
if (!dfn[i])
tarjan(i);
dfs (1, 1);
deal ();
int Q;
read (Q);
while (Q --)
{
int u , v;
read (u, v);
u = vcc[u] , v = vcc[v];
int lca = LCA (u, v);
//cout << u << ' ' << v << ' ' << lca << endl;
//cout << deep[u] << ' ' << deep[v] << ' ' << deep[lca] << endl;
write ((deep[u] + deep[v] - 2 * deep[lca])/2), LF;
}
}
}
- 题目连接:https://www.acwing.com/problem/content/description/365/
- 技巧:建立圆方树,在圆方树上计数
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
const int N = 200005, M = 10000005;
int n , m;
vector <int> Edge[N];
stack <int> st;
int dfn[N] , low[N] , fa[N] , times, type;
struct node {
int t, next;
}edge[M];
int head[N] , cnt;
void add (int f, int t)
{
edge[cnt].t = t;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
void tarjan (int u)
{
dfn[u] = low[u] = ++ times;
st.push(u);
for (auto v : Edge[u])
{
if (!dfn[v])
{
fa[v] = u;
tarjan (v);
low[u] = min (low[u], low[v]);
if (dfn[u] <= low[v])
{
++ type;
while (1)
{
int x = st.top();
add (type, x);
add (x, type);
st.pop();
if (x == v) break;
}
add (type, u);
add (u, type);
}
}
else if (v != fa[u])
low[u] = min (low[u], dfn[v]);
}
}
int siz[N];
ll ans[N];
void dfs (int u, int pa)
{
siz[u] = (u <= n);
int sum = 0;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if (v != pa)
{
dfs (v, u);
sum += siz[v];
siz[u] += siz[v];
}
}
int temp = n - siz[u];
ans[u] = 2ll * (n - 1);
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if (v != pa)
{
ans[u] += 2ll * siz[v] * temp;
temp += siz[v];
}
}
}
int main ()
{
CLOSE;
mem (head, -1);
read (n, m);
type = n;
for (int i = 1 ; i <= m ; i ++)
{
int u , v;
read (u, v);
Edge[u].pb (v);
Edge[v].pb (u);
}
for (int i = 1 ; i <= n ; i ++)
if (!dfn[i])
{
tarjan (i);
dfs (i, i);
}
for (int i = 1 ; i <= n ; i ++) cout << ans[i] << endl;
}
- 题目链接: https://codeforces.com/problemset/problem/1389/G
- e c c ecc ecc缩点之后形成一颗树,同时处理出所有节点的权值以及是否包含关键点。形成一颗树之后,选取一个包含关键点的节点作为根,然后将所有树的非关键节点合并到关键节点上,做树形DP
因为强制要求某个i点为饱和点,所有i子树的点要么无向要么必须指向i,i的父亲必须指向i,所以换根的时候只用考虑一条边贡献即可
第一次dp
d
p
1
[
u
]
+
=
s
u
m
[
u
]
+
m
a
x
(
d
p
1
[
v
]
−
c
,
0
)
dp1[u] += sum[u] + max (dp1[v]-c,0)
dp1[u]+=sum[u]+max(dp1[v]−c,0)
第二次dp
d
p
2
[
u
]
=
m
a
x
(
d
p
1
[
u
]
−
m
a
x
(
d
p
1
[
v
]
−
c
,
0
)
−
c
,
0
)
dp2[u] = max (dp1[u]-max(dp1[v]-c,0)-c, 0)
dp2[u]=max(dp1[u]−max(dp1[v]−c,0)−c,0)
输出所有dp2 即可
对于第二次换根DP , 由于dp1[v]表示子树V的所有关键节点能够到达V的最大值。
d
p
1
[
u
]
−
m
a
x
(
d
p
1
[
v
]
−
c
,
0
)
−
c
dp1[u]-max(dp1[v]-c,0)-c
dp1[u]−max(dp1[v]−c,0)−c的意义是 dp1[u]表示u的子树中所有关键节点到达u的最大贡献,减去v子树的贡献之后
那么剩余节点的贡献就是
d
p
1
[
u
]
−
m
a
x
(
d
p
1
[
v
]
−
c
,
0
)
dp1[u]-max(dp1[v]-c,0)
dp1[u]−max(dp1[v]−c,0)
1.然后如果定向u-v这条边,除V子树外的所有节点都不可能为饱和节点。因为反向了,V的所有节点是指向V的。而定向的意义是
u
−
v
u-v
u−v。
2.如果不定向
u
−
v
u-v
u−v这条边,那么和dp1的讨论方式是一样的.
const int N = 3e5 + 5;
int n , m , k;
int a[N], c[N], w[N];
int head[N], cnt;
struct node {
int t , next , w;
}edge[N << 1];
void add (int f, int t, int w)
{
edge[cnt].t = t;
edge[cnt].w = w;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
int bridge[N << 1];
int dfn[N << 1] ,low[N << 1], times;
void tarjan(int u, int id)
{
dfn[u] = low[u] = ++times;
for(int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t;
if(!dfn[v])
{
tarjan(v,i);
low[u] = min (low[u] , low[v]);
if(low[v] > dfn[u])
bridge[i] = bridge[i^1] = 1;
}
else if(i != (id ^ 1)) // 如果有重边
low[u] = min(low[u], dfn[v]);
}
}
int ecc[N << 1], type;
ll sum[N];
int rt, fa[N], suma[N];
void dfs(int u, int block)
{
suma[block] |= a[u];
sum[block] += c[u];
ecc[u] = block;
for(int i = head[u] ; i != - 1 ; i = edge[i].next)
{
if(!bridge[i])
{
int v = edge[i].t;
if(!ecc[v])
dfs(v,block);
}
}
if (suma[block])
rt = block;
}
vector <pii> Edge[N];
void dfs1 (int u , int pa)
{
for (auto it : Edge[u])
{
int v = it.fi;
if (v != pa)
{
dfs1 (v, u);
suma[u] |= suma[v];
if (suma[v] == 0)
sum[u] += sum[v], fa[v] = u;
}
}
}
ll dp1[N] , dp2[N];
void dfs2 (int u , int pa)
{
for (auto it : Edge[u])
{
int v = it.fi , cc = it.se;
if (v != pa && suma[v])
{
dfs2 (v, u);
dp1[u] += max (dp1[v]-cc,0ll);
}
}
dp1[u] += sum[u];
}
void dfs3 (int u ,int pa)
{
dp2[u] += dp1[u];
for (auto it : Edge[u])
{
int v = it.fi , cc = it.se;
if (v != pa && suma[v])
{
dp2[v] += max (dp2[u]-max(dp1[v]-cc,0ll)-cc, 0ll);
dfs3 (v, u);
}
}
}
int find (int x)
{
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
int main ()
{
CLOSE;
mem (head, -1);
cin >> n >> m >> k;
for (int i = 1 ; i <= k ; i ++)
{
int x;
cin >> x;
a[x] = 1;
}
for (int i = 1 ; i <= n ; i ++)
cin >> c[i], fa[i] = i;
for (int i = 1 ; i <= m ; i ++)
cin >> w[i];
for (int i = 1 ; i <= m ; i ++)
{
int u , v;
cin >> u >> v;
add (u, v , w[i]);
add (v, u , w[i]);
}
for (int i = 1 ; i <= n ; i ++)
if (!dfn[i])
tarjan (i, -1);
for (int i = 1 ; i <= n ; i ++)
if (!ecc[i])
dfs (i, ++ type);
for (int i = 0 ; i < cnt ; i += 2)
{
int u = edge[i].t, v = edge[i+1].t, ww = edge[i].w;
if (ecc[u] != ecc[v])
{
Edge[ecc[u]].pb(mk(ecc[v], ww));
Edge[ecc[v]].pb(mk(ecc[u],ww));
}
}
dfs1 (rt, 0);
dfs2 (rt, 0);
dfs3 (rt, 0);
for (int i = 1 ; i <= n ; i ++)
cout << dp2[find(ecc[i])] << ' ';
cout << endl;
}