题意:
给定一棵有 个结点的树,问对于每个点 ,是否有可能通过移动至多一条边使得原图仍为一棵树并且 为树的重心。
链接:
https://codeforces.com/problemset/problem/708/C
解题思路:
考虑
能成为重心的情况,找到其重儿子
,若重儿子子树大小不超过
,则可成为重心,否则必须进一步考虑。考虑从重儿子子树里移除一部分直接拼接到
,这一部分大小必须为
,即查找
子树中是否存在子树大小在此区间范围。
以
号结点为根考虑求解,讨论
的重儿子取向。若
的重儿子不是父结点方向,这一部分的
可以通过主席树或
求解。若
的重儿子往父结点方向,则查询区间变成
的外部,且从
号结点到
路径上的结点的子树大小相对于
来讲有所变化,静态主席树不支持更新,故用
沿途修改即可。
简化做法:先找到重心
,以它为根考虑其他结点的求解。重心为根,则其他结点的重儿子只可能是父结点方向,并且移除子树的情况只有两种:① 移除
往该结点
方向上的这条边;② 移除
子树内最大的一棵(不大于
)。讨论然后
即可。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
vector<int> G[maxn];
int siz[maxn], son[maxn], mx[maxn], fa[maxn];
int up[maxn], c[maxn], vis[maxn], ans[maxn];
int n;
#define lowb(x) ((x)&(-x))
void update(int x, int val){
while(x <= n) c[x] += val, x += lowb(x);
}
int query(int x){
int ret = 0;
while(x > 0) ret += c[x], x -= lowb(x);
return ret;
}
void dfs1(int u, int f){
siz[u] = 1, son[u] = 0, mx[u] = 0, fa[u] = f;
for(auto &v : G[u]){
if(v == f) continue;
dfs1(v, u);
siz[u] += siz[v];
mx[u] = max(mx[u], siz[v]);
if(siz[v] > siz[son[u]]) son[u] = v;
}
mx[u] = max(mx[u], n - siz[u]);
if(n - siz[u] > siz[son[u]]) up[u] = 1;
update(siz[u], 1);
}
void add(int u, int f, int val){
update(siz[u], val);
for(auto &v : G[u]){
if(v == f || vis[v]) continue;
add(v, u, val);
}
}
void judge(int u){
int l = mx[u] - n / 2, r = n / 2;
int tmp = query(r) - query(l - 1);
// cout << u << " " << ans[u] << " " << l << " " << r << endl;
ans[u] = tmp > 0;
}
void dfs2(int u, int f, int flg){
// cout << u << " ? " << f << " " << flg << endl;
for(auto &v : G[u]){
if(v == f || v == son[u]) continue;
int nsz = n - siz[v];
update(siz[u], -1), update(nsz, 1);
dfs2(v, u, 0);
update(siz[u], 1), update(nsz, -1);
}
if(son[u]){
int nsz = n - siz[son[u]];
update(siz[u], -1), update(nsz, 1);
dfs2(son[u], u, 1), vis[son[u]] = 1;
update(siz[u], 1), update(nsz, -1);
}
add(u, f, -1);
if(up[u]) judge(u);
if(son[u]) vis[son[u]] = 0;
if(!flg) add(u, f, 1);
}
void dfs3(int u, int f, int flg){
for(auto &v : G[u]){
if(v == f || v == son[u]) continue;
dfs3(v, u, 0);
}
if(son[u]) dfs3(son[u], u, 1), vis[son[u]] = 1;
if(!up[u]) judge(u);
add(u, f, 1);
if(son[u]) vis[son[u]] = 0;
if(!flg) add(u, f, -1);
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n;
for(int i = 1; i < n; ++i){
int u, v; cin >> u >> v;
G[u].pb(v), G[v].pb(u);
}
dfs1(1, 0);
dfs2(1, 0, 0);
for(int i = 1; i <= n; ++i) c[i] = 0;
dfs3(1, 0, 0);
for(int i = 1; i <= n; ++i) cout << ans[i] << " "; cout << endl;
return 0;
}
智力打击:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
vector<int> G[maxn];
int siz[maxn], rmx[maxn], ans[maxn];
int n, rmn, rt, mx, mx2;
void dfs1(int u, int f){
rmx[u] = 0, siz[u] = 1;
for(auto &v : G[u]){
if(v == f) continue;
dfs1(v, u);
siz[u] += siz[v];
rmx[u] = max(rmx[u], siz[v]);
}
rmx[u] = max(rmx[u], n - siz[u]);
if(rmx[u] < rmn) rmn = rmx[u], rt = u;
}
void dfs2(int u, int f, int mx){
if(rmx[u] > n / 2) {
ans[u] = rmx[u] - mx <= n / 2;
}
else ans[u] = 1;
for(auto &v : G[u]){
if(v == f) continue;
dfs2(v, u, mx);
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n;
for(int i = 1; i < n; ++i){
int u, v; cin >> u >> v;
G[u].pb(v), G[v].pb(u);
}
rmn = inf, dfs1(1, 0);
rmn = 0, dfs1(rt, 0);
for(auto &v : G[rt]){
if(siz[v] > mx) mx2 = mx, mx = siz[v];
else if(siz[v] > mx2) mx2 = siz[v];
}
ans[rt] = 1;
// cout << rt << endl;
for(auto &v : G[rt]){
int tmp = siz[v] == mx ? mx2 : mx;
if(n - siz[v] <= n / 2) tmp = n - siz[v];
dfs2(v, rt, tmp);
}
for(int i = 1; i <= n; ++i) cout << ans[i] << " "; cout << endl;
return 0;
}