题解 P4219 【[BJOI2014]大融合】
好像没有树剖的题解啊
由于这题没有删边操作
然后又没有强制在线
我们可以把最终形成的森林的形态建出来
我们可以建一个超级root 将森林变成树
我们维护每一阶段的每个点子树大小(节点个数),同时用并查集维护每个点此时的根
我们定义find(x)为这个阶段x的根 (为啥定义的是find呢,因为寻根是并查集操作)
size(x)为这个阶段x的子树大小(节点个数)
A x y操作:
假设x 是y的父节点
我们把x到find(x)的每个点都加上size(y)即可
路径上加一个值,我们可以用树剖来维护
最后再并查集merge一下(x,y)
Q x y操作:
假设x是y的父节点
则答案就是(size(x) - size(y))*(size(y))
这里所要用到的询问,是区间加,单点询问,可以差分后维护树状数组
下面是代码,175ms,写这篇题解的在最优解排名在第一页rk6,应该还是比较快的
using namespace std;
#define MAXN 400005
#define lowbit(x) (x&(-x))
#define LL long long
int n,m;
int f[MAXN];
struct bian
{
int x,y,ls;
}b[MAXN];
int t[MAXN],cnt;
struct aa
{
bool opt;
int x,y;
}p[MAXN];
void print(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
print(x/10);
putchar(x%10+'0');
}
inline int read()
{
register int x = 0 , ch = getchar();
while( !isdigit( ch ) ) ch = getchar();
while( isdigit( ch ) ) x = x * 10 + ch - '0' , ch = getchar();
return x;
}
void jb(int x,int y)//建边
{
cnt ++;
b[cnt].x = x;
b[cnt].y = y;
b[cnt].ls = t[x];
t[x] = cnt;
}
void rd()
{
cin >> n >> m;
for(int i = 1; i <= m; i ++)
{
char ch = 0;
while(ch != 'A' && ch != 'Q') ch = getchar();
if(ch == 'A') p[i].opt = 1;
else p[i].opt = 0;
p[i].x = read();
p[i].y = read();
if(p[i].opt == 1)
{
jb(p[i].x,p[i].y);
jb(p[i].y,p[i].x);
}
}
}
void csh(){//并查集初始化
for(int i = 1; i <= n; i ++) f[i] = i;
}
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
void mg(int x,int y)//mg就是merge
{
f[find(y)] = find(x);
}
int c[MAXN];
void jia(int x,int y) {
for(; x <= n; x += lowbit(x))
c[x] += y;
}
int he(int x)
{
int ans = 0;
for(; x > 0; x -= lowbit(x))
ans += c[x];
return ans;
}
int ht[MAXN],th[MAXN],z[MAXN],s[MAXN],a[MAXN];
int fa[MAXN];
void dfs(int x)
{
s[x] = 1;
int mz = 0;
for(int i = t[x]; i != 0; i = b[i].ls)
{
int y = b[i].y;
if(y != fa[x]) {
fa[y] = x;
dfs(y);
s[x] += s[y];
if(s[y] > mz)
{
mz = s[y];
z[x] = y;
}
}
}
}
void dfs2(int x,int _ht,int _th)
{
ht[x] = _ht;
th[x] = _th;
cnt ++;
a[x] = cnt;
if(z[x])
{
dfs2(z[x],_ht,_th);
}
for(int i = t[x]; i != 0 ; i = b[i].ls)
{
int y = b[i].y;
if(y != fa[x] && y != z[x]) {
dfs2(y,y,_th+1);
}
}
}
void optA(int x,int y,int s)//x到y每个点点权+s
{
while(ht[x] != ht[y])
{
jia(a[ht[x]],s);
jia(a[x]+1,-s);
x = fa[ht[x]];
}
jia(a[y],s);
jia(a[x]+1,-s);
}
signed main()
{
rd();
csh();
for(int i = 1; i <= m; i ++) {
if(p[i].opt == 1) {
mg(p[i].x,p[i].y);
}
}
for(int i = 1; i <= n; i ++)
{
if(find(i) == i) {
jb(i,n+1);
jb(n+1,i);
}
}
n ++;
csh();
dfs(n);
cnt = 0;
dfs2(n,n+1,1);
jia(1,1);
for(int i = 1; i <= m; i ++)
{
int x = p[i].x,y = p[i].y;
if(fa[x] == y) swap(x,y);//确保y的父亲是x
if(p[i].opt == 1)
{
optA(x,find(x),he(a[y]));
mg(x,y);
} else {
LL ans = 0;
int sa = he(a[find(x)]),sb = he(a[y]);
ans = 1ll*(sa-sb)*sb;
print(ans);
puts("");
}
}
return 0;
}
树链剖分写树链剖分,我 写 我 自 己