Cold_Chair的博客

一位蒟蒻的事故记录

树上三角形(idea+lct或splay)

Description:

给定一个n个点的以1为根的树,每个点有一个正整数点权。
有q个操作,每个操作为以下类型之一:
1. 1 u v 询问树上所有在u到v的简单路径的节点(含u,v)中,是否存在三个不同的节点,使得以这三个节点的点权为边长的三条边能够构成一个三角形。
2. 2 u v 将节点u的权值改成v。
3. 3 u v 若节点v不在以节点u为根的子树里,那么令u的父节点为v,否则令v的父节点为u,如果u=v那么忽略这一条操作。
所有操作按输入的顺序进行。
这个题面应该不会看不懂吧。。

题解:

首先你得有个idea,有了这个idea就后面的好办了。

考虑构造一个数列让它没有三角形,显然最优的是斐波拉契数列。

斐波拉契数列增长得还挺快的。

大概46项时就超过了2^31-1。

所以得出结论:
如果路径长度超过46,则一定有解。

小于等于46的话,排个序,看看有没有相邻的3个数满足三角形就行了。

如果没有3操作,我们甚至连数据结构都不用写。

有了三操作,我们发现唯一的难点在于判断y是否在x的子树里。

这个先求出括号序,再用splay即可轻松解决。

splay我竟然去暴力求子树大小,结果TLE了,傻了傻了~

当然lct维护更加轻松,具体的话先makeroot(1,)access(y)一下,如果x和y在一个splay里,则y是x子树里的点,之后的是经典的link-cut

查询的话lct也是棒棒的,代码也短。

虽然这样,为了练手,还是打了splay。

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 5e5 + 5;

int n, q, a[N], f[N], x, y, tp;
int next[N], to[N], final[N], tot;
int z[N][2], td;
int fa[N], t[N][2], siz[N], S, T;

void link(int x, int y) {next[++ tot] = final[x], to[tot] = y, final[x] = tot;}
void dg(int x) {
    z[x][0] = ++ td;
    for(int i = final[x]; i; i = next[i]) dg(to[i]);
    z[x][1] = ++ td;
}
void update(int x) {if(x) siz[x] = 1 + siz[t[x][0]] + siz[t[x][1]];}
int lr(int x) {return t[fa[x]][1] == x;}
void rotate(int x) {
    int y = fa[x], k = lr(x);
    t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;
    fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;
    fa[y] = x; t[x][!k] = y;
    update(y); update(x);
}
void splay(int x, int y) {
    while(fa[x] != y) {
        if(fa[fa[x]] != y)
            rotate(lr(x) == lr(fa[x]) ? fa[x] : x);
        rotate(x);
    }
}
int fz(int x) {return !x ? 0 : 1 + fz(t[x][0]) + fz(t[x][1]);}
int fl(int x) {return t[x][0] ? fl(t[x][0]) : x;}
int fr(int x) {return t[x][1] ? fr(t[x][1]) : x;}

int d[N], bz[N];

int main() {
    freopen("triangle.in", "r", stdin);
    freopen("triangle.out", "w", stdout);
    scanf("%d %d", &n, &q);
    fo(i, 1, n) scanf("%d", &a[i]);
    fo(i, 2, n) scanf("%d", &f[i]), link(f[i], i);

    dg(1);
    S = td + 1; T = S + 1;
    fo(i, 1, T) siz[i] = 1;
    fa[1] = S; t[S][1] = 1;
    fo(i, 2, td) fa[i] = i - 1, t[i - 1][1] = i;
    fa[T] = td; t[td][1] = T;
    fo(i, 1, T) splay(i, 0);

    fo(tm, 1, q) {
        scanf("%d %d %d", &tp, &x, &y);
        if(tp == 1) {
            int p = x;
            fo(i, 1, 46) {
                if(!p) break;
                bz[p] = tm;
                p = f[p];
            }
            int ans = 1;
            p = y;
            fo(i, 1, 46) {
                if(!p) break;
                if(bz[p] == tm) {
                    ans = 0; break;
                }
                p = f[p];
            }

            if(ans) {printf("Y\n"); continue;}

            int lca = p;
            d[0] = 1; d[1] = a[lca];
            p = x; while(p != lca) d[++ d[0]] = a[p], p = f[p];
            p = y; while(p != lca) d[++ d[0]] = a[p], p = f[p];
            sort(d + 1, d + d[0] + 1);
            fo(i, 3, d[0])
                if(d[i - 2] > d[i] - d[i - 1]) {
                    ans = 1; break;
            }

            if(ans) printf("Y\n"); else printf("N\n");
        } else
        if(tp == 2) {
            a[x] = y;
        } else {
            if(x == y) continue;

            splay(z[x][0], 0); int xz = siz[t[z[x][0]][0]];
            splay(z[x][1], 0); int xz2 = siz[t[z[x][1]][0]];
            splay(z[y][0], 0); int yz = siz[t[z[y][0]][0]];
            if(xz < yz && yz < xz2) swap(x, y);
            f[x] = y;

            splay(z[x][0], 0); int xl = fr(t[z[x][0]][0]);
            splay(z[x][1], 0); int xr = fl(t[z[x][1]][1]);
            splay(xl, 0); splay(xr, xl);
            int h = t[xr][0]; fa[h] = t[xr][0] = 0;
            splay(z[y][1], 0); int yl = fr(t[z[y][1]][0]);
            splay(yl, 0); splay(z[y][1], yl);
            t[z[y][1]][0] = h; fa[h] = z[y][1];
        }
    }
}
阅读更多
版权声明:博主写博客也挺不容易,转载当然阔以,记得先吱一声~ https://blog.csdn.net/Cold_Chair/article/details/80355485
个人分类: lct splay
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

树上三角形(idea+lct或splay)

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭