以前看到这道题不知所云,今天终于把它A啦…
Tags:树链剖分 树状数组
一棵树是胡须树当且仅当除根结点外的结点都只有一个儿子。胡须树的每条边要么是黑色要么是白色。
给定一棵胡须树,树结点数 V ∈ [ 2 , 1 0 5 ] V \in [2, 10^5] V∈[2,105]。有两种操作(操作次数 q ∈ [ 1 , 1 0 5 ] q\in[1, 10^5] q∈[1,105]):
3
1 2
2 3
7
3 1 2
3 1 3
3 2 3
2 2
3 1 2
3 1 3
3 2 3
1
2
1
1
-1
-1
6
1 5
6 4
2 3
3 5
5 6
6
3 3 4
2 5
3 2 6
3 1 2
2 3
3 3 1
3
-1
3
2
啊哈哈,当初看到这道题的时候还无从下手,现在发现其实就是一个弱化版的边权树剖…
不必动用两次dfs,直接把胡须树的每条胡须当成链就行啦。
然后这里是单点修改+区间查询,那当然是上树状数组~
另外一些小细节注意一下就行(给边编号啥的)
#include <cstdio>
#define MAX(a, b) ((a)>(b)?(a):(b))
#define SWAP(a, b) do{auto _t=a; a=b; b=_t;}while(0)
constexpr int MN(1e5+7);
enum Color
{
BLACK, WHITE
};
struct Ed
{
int u, v;
Ed *next;
} ed[MN << 1], *head[MN];
int tot;
#define edd(uu, vv) ed[++tot].next=head[uu], ed[tot].u=uu, ed[tot].v=vv, head[uu]=tot+ed
int deg[MN]; // 入度,用来找根(入度最大的)
int top_color[MN]; // 存储top结点和根结点之间的边的颜色。边的编号是所连接两点编号的更大者(根编号是0)。
int color[MN]; // 存储其他普通边的颜色。边的编号是所连接两点编号的更大者。
int now_top;
int top[MN], id[MN], time;
void dfs(const int u, const int fa)
{
top[u] = now_top;
id[u] = ++time;
for (Ed *p=head[u]; p; p=p->next)
if (p->v != fa)
dfs(p->v, u);
}
namespace BIT
{
inline constexpr int lowbit(const int x)
{
return x & -x;
}
int b[MN];
inline void add(int i, const int v)
{
while (i <= time)
b[i] += v, i += lowbit(i);
}
inline int sum(int i)
{
int s = 0;
while (i > 0)
s += b[i], i -= lowbit(i);
return s;
}
inline int sum(const int l, const int r)
{
return sum(r) - sum(l-1);
}
}
int main()
{
int V;
scanf("%d", &V);
int root = 0;
int u, v;
while (--V)
{
scanf("%d %d", &u, &v);
edd(v, u);
edd(u, v);
if (++deg[u] > deg[root])
root = u;
if (++deg[v] > deg[root])
root = v;
}
for (Ed *p=head[root]; p; p=p->next)
{
now_top = p->v;
dfs(p->v, root); // 对胡须树的一条胡须链标id号和top
}
int q;
scanf("%d", &q);
while (q--)
{
int op;
scanf("%d", &op);
if (op == 3)
{
int color_sum = 0, dis = 0;
scanf("%d %d", &u, &v);
if (u == v)
puts("0");
else
{
if (top[u]==top[v])
{
int idu = id[u], idv = id[v];
if (idu > idv)
SWAP(idu, idv);
color_sum = BIT::sum(idu+1, idv), dis = idv - idu; // idu加一是因为树编号的方式是取连接两点的编号更大者
}
else
{
if (u != root)
color_sum += BIT::sum(id[top[u]], id[u]) + top_color[u], dis += id[u] - id[top[u]] + 1;
if (v != root)
color_sum += BIT::sum(id[top[v]], id[v]) + top_color[v], dis += id[v] - id[top[v]] + 1;
}
if (color_sum)
puts("-1");
else
printf("%d\n", dis);
}
}
else
{
int k;
scanf("%d", &k);
const auto &edge = ed[k << 1]; // 双向边,边的实际编号是逻辑编号*2
if (edge.u==root)
top_color[edge.v] ^= 1;
else if (edge.v==root)
top_color[edge.u] ^= 1;
else
{
const int ed_id = MAX(id[edge.u], id[edge.v]);
BIT::add(ed_id, color[ed_id] == BLACK ? +1 : -1);
color[ed_id] ^= 1;
}
}
}
return 0;
}