线段树--区间合并hdu--1540

本文介绍了一种使用线段树的数据结构来模拟抗日战争期间地道战的连接状态的方法。通过单点更新来处理村庄被破坏及重建的情况,有效地解决了军队指挥官询问关于村庄间连接状态的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tunnel Warfare

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8569    Accepted Submission(s): 3301


Problem Description
During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly connected with two neighboring ones.

Frequently the invaders launched attack on some of the villages and destroyed the parts of tunnels in them. The Eighth Route Army commanders requested the latest connection state of the tunnels and villages. If some villages are severely isolated, restoration of connection must be done immediately!
 

Input
The first line of the input contains two positive integers n and m (n, m ≤ 50,000) indicating the number of villages and events. Each of the next m lines describes an event.

There are three different events described in different format shown below:

D x: The x-th village was destroyed.

Q x: The Army commands requested the number of villages that x-th village was directly or indirectly connected with including itself.

R: The village destroyed last was rebuilt.
 

Output
Output the answer to each of the Army commanders’ request in order on a separate line.
 

Sample Input
7 9 D 3 D 6 D 5 Q 4 Q 5 R Q 4 R Q 4
 

Sample Output
1 0 2 4

线段树本就擅长于处理区间问题,这题思考很久之后,我想到了做法,最开始整个区间是连续的,当破坏了一个村庄之后,当前区间的从左数和从右数的连续区间要更新,L数组用来存当前区间从左数最长区间,R数组用来存当前区间从右到左最长连续区间,S数组是当前区间最大连续区间,lc和rc是端点的区间。而这道题是单点更新,不需要标记,只要往上更新就行了

#include <iostream>
#include <stdio.h>
#include <string.h>
#define siz 50005
#define lson(x) ((x)<<1)
#define rson(x) (((x)<<1)|1)

using namespace std;
int sets[siz<<2],lc[siz<<2],rc[siz<<2];
int L[siz<<2],R[siz<<2],S[siz<<2];
int n,m;
int sta[siz];
int length(int root){
    return rc[root]-lc[root]+1;
}
void maintain(int root,int v){
    L[root]=R[root]=S[root]=(v?length(root):0);///当前状态当前区间更新为有空房间和无空房间
}
void pushup(int root){
    S[root]=max(max(S[lson(root)],S[rson(root)]),R[lson(root)]+L[rson(root)]);
    L[root]=L[lson(root)]+(L[lson(root)]==length(lson(root))?L[rson(root)]:0);
    ///如果左儿子全是连续区间,则当前区间右儿子从左数的连续区间是从左边数的连续区间的一部分
    R[root]=R[rson(root)]+(R[rson(root)]==length(rson(root))?R[lson(root)]:0);
    ///同理
}
void build(int root,int l,int r){
    lc[root]=l;
    rc[root]=r;
    if(l==r){
        maintain(root,1);///建树,1代表整个区间连续
        return ;
    }
    int mid=(lc[root]+rc[root])>>1;
    build(lson(root),l,mid);
    build(rson(root),mid+1,r);
    pushup(root);
}
void modify(int u,int k,int v){///类似于普通线段树的单点更新
    //cout<<u<<" "<<k<<"^^^"<<lc[u]<<endl;
    if(lc[u]==rc[u]&&lc[u]==k){
        maintain(u,v);
        return ;
    }
    int mid=(lc[u]+rc[u])>>1;
    //cout<<mid<<" "<<lc[u]<<" "<<rc[u]<<endl;
    if(k<=mid) modify(lson(u),k,v);
    else modify(rson(u),k,v);
    pushup(u);
}
int query(int rt,int k){
    if(lc[rt]==rc[rt]&&lc[rt]==k){
        return S[rt];
    }
    int mid=(lc[rt]+rc[rt])>>1;
    if(k<=mid){
        if(mid-R[lson(rt)]+1<=k){///最左边比k小,说明k在从右数连续区间内
            return R[lson(rt)]+L[rson(rt)];///加上左边的区间
        }else{
            return query(lson(rt),k);
        }
    }else{
        if(mid+1+L[rson(rt)]-1>=k){///同理
            return L[rson(rt)]+R[lson(rt)];
        }else{
            return query(rson(rt),k);
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
    build(1,1,n);
    int ind=0;
    while(m--){
        //getchar();
        char c[2];
        int v;
        scanf("%s",c);
        if(c[0]=='D'){
            scanf("%d",&v);
            sta[ind++]=v;
            modify(1,v,0);///村庄被销毁,断点
        }
        else if(c[0]=='Q'){
                scanf("%d",&v);
            int ans=query(1,v);
            printf("%d\n",ans);
        }
        else{
            if(ind>0){
                v=sta[--ind];
                modify(1,v,1);///1修复村庄
            }

        }
       // cout<<1<<endl;
    }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值