bzoj1180: [CROATIAN2009]OTOCI

6 篇文章 0 订阅

Description
给出n个结点以及每个点初始时对应的权值wi。起始时点与点之间没有连边。有3类操作: 1、bridge A B:询问结点A与结点B是否连通。如果是则输出“no”。否则输出“yes”,并且在结点A和结点B之间连一条无向边。 2、penguins A X:将结点A对应的权值wA修改为X。 3、excursion A B:如果结点A和结点B不连通,则输出“impossible”。否则输出结点A到结点B的路径上的点对应的权值的和。给出q个操作,要求在线处理所有操作。数据范围:1<=n<=30000, 1<=q<=300000, 0<=wi<=1000。

Input
第一行包含一个整数n(1<=n<=30000),表示节点的数目。第二行包含n个整数,第i个整数表示第i个节点初始时对应的权值。第三行包含一个整数q(1<=n<=300000),表示操作的数目。以下q行,每行包含一个操作,操作的类别见题目描述。任意时刻每个节点对应的权值都是1到1000的整数。

Output
输出所有bridge操作和excursion操作对应的输出,每个一行。

Sample Input
5
4 2 4 5 6
10
excursion 1 1
excursion 1 2
bridge 1 2
excursion 1 2
bridge 3 4
bridge 3 5
excursion 4 5
bridge 1 3
excursion 2 4
excursion 2 5

Sample Output
4
impossible
yes
6
yes
yes
15
yes
15
16

看起来就是LCT啊..

求和x到y的伪代码:

getsum(x,y)
access(x) splay(x) rev[x]^=1
access(y) splay(y)
reutrn sum[y]

修改权值的伪代码:

change(x,k)
access(x) splay(x) rev[x]^=1
val[x]=k
update(x)

那么就是很显而易见的标算了啊..

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 30010;
int val[Maxn], sum[Maxn], c[Maxn][2], rev[Maxn], fa[Maxn];
int n, m;
char s[11];
bool is_root ( int x ){ return c[fa[x]][0] != x && c[fa[x]][1] != x; }
void update ( int x ){
    int lc = c[x][0], rc = c[x][1];
    sum[x] = sum[lc]+val[x]+sum[rc];
}
void push_down ( int x ){
    if ( rev[x] ){
        rev[x] = 0;
        int lc = c[x][0], rc = c[x][1];
        swap ( c[x][0], c[x][1] );
        rev[lc] ^= 1; rev[rc] ^= 1;
    }
}
int st[Maxn], tp;
void prep ( int x ){
    tp = 0; int i;
    for ( i = x; !is_root (i); i = fa[i] ) st[++tp] = i;
    st[++tp] = i;
    for ( i = tp; i >= 1; i -- ) push_down (st[i]);
}
void rotate ( int x ){
    int y = fa[x], z = fa[y], l, r;
    if ( c[y][0] == x ) l = 0; else l = 1; r = l^1;
    if ( !is_root (y) ){ if ( c[z][0] == y ) c[z][0] = x; else c[z][1] = x; }
    fa[x] = z; fa[y] = x; fa[c[x][r]] = y;
    c[y][l] = c[x][r]; c[x][r] = y;
    update (y); update (x);
}
void splay ( int x ){
    prep (x);
    while ( !is_root (x) ){
        int y = fa[x], z = fa[y];
        if ( !is_root (y) ){
            if ( (c[y][0]==x)^(c[z][0]==y) ) rotate (x);
            rotate (y);
        }
        rotate (x);
    }
}
void access ( int x ){
    int t = 0;
    while (x){
        splay (x);
        c[x][1] = t;
        update (x);
        t = x;
        x = fa[x];
    }
}
int find_root ( int x ){
    access (x);
    splay (x);
    while (c[x][0]) x = c[x][0];
    return x;
}
void make_root ( int x ){ access (x); splay (x); rev[x] ^= 1; }
void link ( int x, int y ){ make_root (x); fa[x] = y; }
int get_sum ( int x, int y ){
    make_root (x);
    access (y); splay (y);
    return sum[y];
}
void change ( int x, int k ){
    make_root (x);
    val[x] = k;
    update (x);
}
int main (){
    int i, j, k;
    scanf ( "%d", &n );
    for ( i = 1; i <= n; i ++ ){ scanf ( "%d", &val[i] ); sum[i] = val[i]; }
    scanf ( "%d", &m );
    for ( i = 1; i <= m; i ++ ){
        getchar ();
        scanf ( "%s", s+1 );
        int x, y;
        scanf ( "%d%d", &x, &y );
        if ( s[1] == 'b' ){
            if ( find_root (x) == find_root (y) ) printf ( "no\n" );
            else { printf ( "yes\n" ); link ( x, y ); }
        }
        else if ( s[1] == 'p' ) change ( x, y );
        else {
            if ( find_root (x) != find_root (y) ) printf ( "impossible\n" );
            else printf ( "%d\n", get_sum ( x, y ) );
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值