【JZOJ4090】树上三角形(triangle)

34 篇文章 0 订阅
12 篇文章 0 订阅
这是一道关于树上三角形查询与修改的问题。给定一棵以1为根的树,每个节点都有一个正整数权值。操作包括询问在指定路径上的节点是否能构成三角形,修改节点权值,以及调整树的结构。对于20%的数据,n, q <= 1000;对于30%的数据,每个操作t <= 2;对于100%的数据,n <= 100000, q <= 200000。解决方案可以通过LCT(线段树的懒惰传播)实现,对于节点数量较少的情况可以使用模拟。" 114556304,10540062,使用WebSocket实现在SpringBoot中服务器向客户端推送消息,"['Java', 'SpringBoot', 'WebSocket', '服务器推送']
摘要由CSDN通过智能技术生成

problem

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那么忽略这一条操作。
所有操作按输入的顺序进行。
这个题面应该不会看不懂吧。。

Input

第一行有两个正整数n,q,表示树的节点数和操作数。
第二行n个正整数,表示每个节点的点权。
第三行有n-1个正整数,第i个数表示节点i+1的父节点编号,保证所给定的是一棵树。
接下来q行,每行三个正整数t u v,意义如问题描述中所示。

Output

对每个询问输出一行表示答案,“Y”表示有解,“N”表示无解。

Sample Input

5 5
1 2 3 4 5
1 2 3 1
1 1 3
1 4 5
2 1 4
1 2 5
1 2 3

Sample Output

N
Y
Y
N

Data Constraint

对于20%的数据,保证n,q<=1000
对于另外30%的数据,保证对于每个操作,t<=2
对于再另外的30%的数据,保证询问操作不超过20次。
对于100%的数据,保证n<=100000 q<=200000 每个节点的权值<=(2^31)-1


analysis

  • 80% 80 % 以下的数据都是模拟什么的

  • 正解暴力LCT……

  • 思考把 x,y x , y 之间的节点权值拉出来升序排序,共有 k k 个点
    能够成三角形就满足a[i]+a[i+1]>a[i+2](1<=i<=k2)

  • 那么最长不满足这样的东西就是斐波那契数列

  • 而节点权值都在 int i n t 范围内,所以 x,y x , y 之间节点数超过 50 50 个就一定合法

  • 维护查询什么的直接上LCT

  • 判断 x,y x , y 的话,就 makeroot(y),access(x) m a k e r o o t ( y ) , a c c e s s ( x ) ,再用 getroot g e t r o o t 判断

  • 查询 x,y x , y 的话,就 makeroot(y),access(x),splay(x,0) m a k e r o o t ( y ) , a c c e s s ( x ) , s p l a y ( x , 0 ) ,再bfs遍历整棵splay就好


code

#include<bits/stdc++.h>
#define MAXN 100001

using namespace std;

int t[MAXN][2];
int b[MAXN],fat[MAXN],fa[MAXN],pf[MAXN],st[MAXN];
int n,q,tot;

struct node 
{
    long long val;
    int size,rev;
}a[MAXN];

bool cmp(int x,int y)
{
    return a[x].val<a[y].val;
}

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || '9'<ch)
    {
        if (ch=='-')f=-1;
        ch=getchar();   
    }
    while ('0'<=ch && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

long long readll()
{
    long long x=0,f=1;
    char ch=getchar();
    while (ch<'0' || '9'<ch)
    {
        if (ch=='-')f=-1;
        ch=getchar();   
    }
    while ('0'<=ch && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

void reverse(int x) 
{
    if(x)
    {
        a[x].rev^=1;
        swap(t[x][0],t[x][1]);
    }
}

void down(int x)
{
    if (a[x].rev)
    {
        reverse(t[x][0]),reverse(t[x][1]);
        a[x].rev=0;
    }
}

void update(int x)
{
    if (x)
    {
        a[x].size=a[t[x][0]].size+a[t[x][1]].size+1;
    } 
}

void downdata(int x)
{
    st[0]=0;
    while (x)st[++st[0]]=x,x=fa[x];
    while (st[0])down(st[st[0]--]);
}

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;    
    t[x][!k]=y;

    fa[y]=x,pf[x]=pf[y];
    update(y),update(x);
}

void splay(int x, int y) 
{
    downdata(x);
    while (fa[x]!=y)
    {
        if (fa[fa[x]]!=y)
        {
            if (lr(x)==lr(fa[x]))rotate(fa[x]); 
            else rotate(x);
        } 
        rotate(x);
    }
}

void access(int x) 
{
    for (int y=0;x;update(x),y=x,x=pf[x])
    {
        splay(x,0);
        fa[t[x][1]]=0;
        pf[t[x][1]]=x;
        t[x][1]=y;
        fa[y]=x;
        pf[y]=0;
    }
}

void makeroot(int x) 
{
    access(x); 
    splay(x,0); 
    reverse(x);
}

int getroot(int x)
{
    while (fa[x])x=fa[x];
    return x;
}

bool judge(int x,int y)
{
    makeroot(x);
    access(y);
    splay(x,0);
    return getroot(y)==x;
}

void link(int x,int y) 
{
    makeroot(x); 
    pf[x]=y;
}

void cut(int x,int y) 
{
    makeroot(x); 
    access(y);
    splay(x,0);
    t[x][1]=fa[y]=pf[y]=0;
    update(x);
}

void bfs(int x)
{
    queue<int> que;
    while (!que.empty())que.pop();
    que.push(x),b[tot=1]=x;
    while (!que.empty())
    {
        int now=que.front();
        que.pop();
        for (int i=0;i<=1;i++)
        {
            if (t[now][i])que.push(t[now][i]),b[++tot]=t[now][i];
            if (tot>50)break;
        }
        if (tot>50)break;
    }
}

int main()
{
    freopen("triangle.in","r",stdin);
    freopen("triangle.out","w",stdout);

    n=read(),q=read();
    for (int i=1;i<=n;i++)a[i].val=readll();
    for (int i=2;i<=n;i++)
    {
        fat[i]=read();
        link(i,fat[i]);
        update(i);
    }
    while (q--)
    {
        int z=read(),x=read();
        if (z==1)
        {
            int y=read();
            makeroot(y);
            access(x);
            splay(x,0);
            bfs(x);
            if (tot>50)printf("Y\n");
            else
            {
                bool bz=0;
                sort(b+1,b+tot+1,cmp);
                for (int i=1;i<=tot-2;i++)
                {
                    if (a[b[i]].val+a[b[i+1]].val>a[b[i+2]].val)
                    {
                        bz=1;
                        break;
                    }
                }
                printf(bz?"Y\n":"N\n");
            }
        }
        else if (z==2)a[x].val=readll();
        else
        {
            int y=read();
            if (x==y)continue;
            makeroot(1);
            access(y);
            splay(x,0);
            if (getroot(y)==x)
            {
                cut(y,fat[y]),link(y,x);
                fat[y]=x;
            }
            else 
            {
                cut(x,fat[x]),link(x,y);
                fat[x]=y;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值