jzoj 5847.【省选模拟2018.8.24】Graph lct+并查集

Description
小 G 喜欢四处旅游,但是由于城市发展速度太快,小 G 去旅游时经常计划赶不上变化。
小 G 发现自己的旅行计划一共涉及 N N 个城市,每个城市初始时有一个欢乐度 Wi,一共有三种影响旅行计划的事件可能发生:
1 a b 1   a   b 新建一条连接 a,b a , b 两座城市的边。
2 a b 2   a   b a a 城市的欢乐度改为 b
3 a b 3   a   b 进行一次 a a 城市到 b 城市的旅行。
对于每次旅行, 小 G 会将每一条边任意定向,然后从 a a 出发经过一些点到达 b,在这个过程中,小 G 可以经过一个城市多次,小 G 想知道自己经过的所有城市的欢乐度之和的最大值是多少;若 a a 城市无法到达 b 城市,输出 1 − 1

Input
第一行两个正整数 N,M N , M ,表示城市数和操作数。
接下来一行 N N 个非负整数,第 i 个数表示 i i 城市的欢乐度 Wi
接下来 M 行每行三个正整数 op,a,b o p , a , b ,表示一次操作。

Output
对于每个 3 操作, 输出一行一个整数, 表示答案。

Sample Input

5 11
94 37 60 30 24
3 5 4
3 4 1
1 1 4
3 1 1
3 5 4
1 2 5
2 1 17
1 4 5
3 2 3
1 2 3
3 3 5

Sample Output

-1
-1
94
-1
-1
121

Data Constraint

对于 15% 的数据, N,M10 N , M ≤ 10
对于 25% 的数据, N,M100 N , M ≤ 100
对于 45% 的数据, N,M1000 N , M ≤ 1000
对于 70% 的数据, N,M10000 N , M ≤ 10000
对于 100% 的数据, N,M150000 N , M ≤ 150000 Wi10000 W i ≤ 10000

分析:
如果形成了一个环,那么可以缩成一个点,这个点的权值就是这个连通分量的答案。然后相当于找树上路径最大值,考虑 lct l c t 维护。
当我们连成一个环时,我们保留一个代表点,维护一个并查集,使原来路径上所有点指向这个点,把他们的权值都给这个点,然后把整棵 splay s p l a y 的边弄断。然后每次找一个点父亲时,要在并查集上 getfa g e t f a 一下,顺便把这个点的父亲指向 getfa g e t f a 得到的点。

bzoj 2959和这题差不多,但是bzoj上二操作是修改为 b b ,jzoj上数据是增加b,题意又是改为 b b <script type="math/tex" id="MathJax-Element-1252">b</script>。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>

const int maxn=150007;

using namespace std;

int n,m,x,y,op;
int p[maxn],acc[maxn];

struct node{
    int l,r,fa;
    int data,sum;
    int rev;
}t[maxn];

int getfa(int x)
{
    if (!p[x]) return x;
    return (p[x]=getfa(p[x]));
}

void union1(int x,int y)
{
    int u=getfa(x),v=getfa(y);
    if (u==v) return;
    p[v]=u;
}

void updata(int x)
{
    t[x].sum=t[x].data+t[t[x].l].sum+t[t[x].r].sum;
}

bool isroot(int x)
{
    t[x].fa=getfa(t[x].fa);
    return ((t[t[x].fa].l!=x) && (t[t[x].fa].r!=x));
}

void rttr(int x)
{
    int y=t[x].l;
    t[x].l=t[y].r;
    if (t[y].r) t[t[y].r].fa=x;
    t[x].fa=getfa(t[x].fa);
    if (x==t[t[x].fa].l) t[t[x].fa].l=y;
    else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
    t[y].fa=getfa(t[y].fa);
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[y].r=x;
    updata(x); updata(y);
}

void rttl(int x)
{
    int y=t[x].r;
    t[x].r=t[y].l;
    if (t[y].l) t[t[y].l].fa=x;
    t[x].fa=getfa(t[x].fa);
    if (x==t[t[x].fa].l) t[t[x].fa].l=y;
    else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
    t[y].fa=getfa(t[y].fa);
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[y].l=x;
    updata(x); updata(y);
}

void remove(int x)
{
    if (!isroot(x)) remove(t[x].fa=getfa(t[x].fa));
    if (t[x].rev)
    {
        t[x].rev^=1;
        swap(t[x].l,t[x].r);
        if (t[x].l) t[t[x].l].rev^=1;
        if (t[x].r) t[t[x].r].rev^=1;
    }
}

void splay(int x)
{
    remove(x);
    while (!isroot(x))
    {
        int p=t[x].fa=getfa(t[x].fa),g=t[p].fa=getfa(t[p].fa);
        if (isroot(p))
        {
            if (x==t[p].l) rttr(p);
                      else rttl(p);
        }
        else
        {
            if (x==t[p].l)
            {
                if (p==t[g].l) rttr(p),rttr(g);
                          else rttr(p),rttl(g);
            }
            else
            {
                if (p==t[g].l) rttl(p),rttr(g);
                          else rttl(p),rttl(g);
            }
        }
    }
}

void access(int x)
{
    int y=0;
    while (x)
    {
        splay(x);
        t[x].r=y;
        updata(x);
        y=x,x=t[x].fa=getfa(t[x].fa);
    }
}

void makeroot(int x)
{
    access(x);
    splay(x);
    t[x].rev^=1;
}

void link(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    t[x].fa=y;
}

void solve(int x,int root)
{
    if (x!=root) t[root].data+=t[x].data;
    union1(root,x);
    if (t[x].l) solve(t[x].l,root);
    if (t[x].r) solve(t[x].r,root);
    t[x].l=0;
    t[x].r=0;
}

int find(int x)
{
    if (!acc[x]) return x;
    return (acc[x]=find(acc[x]));
}

void union2(int x,int y)
{
    int u=find(x),v=find(y);
    if (u==v) return;
    acc[u]=v;
}

int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&t[i].data);
        t[i].sum=t[i].data;
    }           
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&op,&x,&y);  
        if (op==1)
        {
            x=getfa(x); y=getfa(y);         
            if (find(x)!=find(y))
            {
                link(x,y);
                union2(x,y);
            }
            else
            {
                makeroot(x);
                access(y);
                splay(y);
                solve(y,y);
            }
        }
        if (op==2)
        {
            x=getfa(x);
            t[x].data+=y;
            t[x].sum+=y;            
        }
        if (op==3)
        {
            x=getfa(x); y=getfa(y); 
            if (find(x)!=find(y)) printf("-1\n");
            else
            {               
                makeroot(x);
                access(y);
                splay(y);
                printf("%d\n",t[y].sum);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值