CF2187C 思路分享(博弈论,树上启发式合并,树状数组)
https://codeforces.com/problemset/problem/2187/C
题意
给定 \(n\) 个点构成有向图,对于 \(1\le i \lt n\),有 \(i \rightarrow i+1\) 的边.
另有 \(m\) 条边 \(u \rightarrow v\),均满足 \(u \lt v\).
另外,对于给定边 \(u_1 \rightarrow v_1\) 和 \(u_2 \rightarrow v_2\),不存在 \(u_1 \lt u_2 \lt v_1 \lt v_2\) 的情况.
\(A\) 和 \(B\) 进行游戏,每回合,\(A\) 必须沿着一条边走一步,\(B\) 可以选择不走,当 \(A\) 和 \(B\) 位于同一位置时,\(B\) 立即获胜;当 \(A\) 位于 \(n\) 时,\(A\) 获胜.
定义 \(f(x,y)\) 为:\(A\) 初始位于 \(x\),\(B\) 初始位于 \(y\),若 \(B\) 无法获胜,\(f(x,y)=0\);否则 \(f(x,y)= B\) 获胜需要走的最少步数.
求
\[\sum_{x,y,x\ne y}{f(x,y)}
\]
\(2\le n \le 2\cdot 10^5\).
思路
首先,\(A\) 和 \(B\) 每步一定会走最远的边,因此只保留最远的边,变成一棵树,\(B\) 获胜条件是 \(depth_y \le depth_x\).
即计算
\[\sum_{x,y,x\ne y}{[depth_y \le depth_x](depth_y-depth_{LCA(x,y)})}
\]
将上式化成有序的形式
\[\sum_{x\lt y}{\min(depth_x,depth_y)-depth_{LCA(x,y)}}+\sum_{x\lt y}{[depth_x=depth_y](depth_x-depth_{LCA(x,y)})}
\]
使用树上启发式合并,用树状数组维护区间的 \(depth\) 之和以及区间的 \(depth\) 数量.
左边部分看作是 \(\le depth_y\) 和 \(\gt depth_y\) 的两部分,讨论求和即可.
时间复杂度 \(\mathcal{O}(n\log^2 n)\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
class fenwick{
public:
int n;
vector<ll> a;
fenwick(int _n){
n = _n;
a.assign(n+1,0);
}
void build(vector<ll>& b){
a = b;
for (int i=1;i<=n;i++){
int j = i+(i&-i);
if (j<=n){
a[j] += a[i];
}
}
}
void update(int x,ll val){
if (x<1 || x>n) return;
for (int i=x;i<=n;i+=i&-i){
a[i] += val;
}
}
ll query(int x){
if (x<1 || x>n) return 0;
ll res = 0;
for (int i=x;i>=1;i-=i&-i){
res += a[i];
}
return res;
}
ll query_range(int l,int r){
if (l<1 || l>n || r<1 || r>n || l>r) return 0;
return query(r)-query(l-1);
}
};
void solve(){
int n,m;
cin >> n >> m;
vector<int> par(n+1,-1);
for (int i=1;i<n;i++){
par[i] = i+1;
}
for (int i=0;i<m;i++){
int u,v;
cin >> u >> v;
par[u] = max(par[u],v);
}
vector<vector<int>> adj(n+1);
for (int i=1;i<n;i++){
adj[par[i]].push_back(i);
}
vector<int> sz(n+1),son(n+1,-1),depth(n+1);
depth[n] = 1;
function<void(int)> dfs = [&](int u){
sz[u] = 1;
int pos = -1;
int mx = 0;
for (auto& v:adj[u]){
depth[v] = depth[u]+1;
dfs(v);
sz[u] += sz[v];
if (sz[v]>mx){
mx = sz[v];
pos = v;
}
}
son[u] = pos;
};
dfs(n);
fenwick fw1(n),fw2(n);
vector<ll> ctr(n+1);
function<void(int,int,int)> add = [&](int u,int t,int op){
if (op==0){
ctr[t] += fw1.query_range(1,depth[u])-depth[t]*fw2.query_range(1,depth[u]);
ctr[t] += (fw2.query_range(depth[u]+1,n)+fw2.query_range(depth[u],depth[u]))*(depth[u]-depth[t]);
}
else{
fw1.update(depth[u],depth[u]);
fw2.update(depth[u],1);
}
for (auto& v:adj[u]){
add(v,t,op);
}
};
function<void(int)> sub = [&](int u){
fw1.update(depth[u],-depth[u]);
fw2.update(depth[u],-1);
for (auto& v:adj[u]){
sub(v);
}
};
function<void(int,int)> dfs2 = [&](int u,int keep){
for (auto& v:adj[u]){
if (v!=son[u]){
dfs2(v,0);
ctr[u] += ctr[v];
}
}
if (son[u]!=-1){
dfs2(son[u],1);
ctr[u] += ctr[son[u]];
}
for (auto& v:adj[u]){
if (v!=son[u]){
add(v,u,0);
add(v,u,1);
}
}
ctr[u] += fw1.query_range(1,depth[u])-depth[u]*fw2.query_range(1,depth[u]);
fw1.update(depth[u],depth[u]);
fw2.update(depth[u],1);
if (keep==0){
sub(u);
}
};
dfs2(n,1);
cout << ctr[n] << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号