데이터 구조 (선분 트 리 입문 상세 소개) (단점 업데이트) (구조 체)

8180 단어 데이터 구조
(선분 수 입문)
HDU 1166 적군 포진 (단점 업데이트) (구조 체 형식)
제목: T, T 테스트 샘플 입력
매번 n 을 입력 할 때마다 n 개의 데이터 가 있다 는 것 을 설명 합 니 다.n 개의 데 이 터 를 입력 하 십시오.
Query a b 입력: a b 사이 의 합 을 조회 합 니 다.
Add i x 입력: i 에 x 개 수 를 추가 합 니 다.
Sub i x 입력: i 에서 x 개 삭제
입력 끝
예:
1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3:  출력 6 (1 + 2 + 3)
Add 3 6:     배열 이 1, 2, 9, 4, 5, 6, 7, 8, 9, 10 으로 바 뀌 었 습 니 다.
Query 2 7:  출력 33 (2 + 9 + 4 + 5 + 6 + 7)
Sub 10 2:   1, 2, 9, 4, 5, 6, 7, 8, 9, 8.
Add 6 3:     1, 2, 9, 4, 5, 9, 7, 8, 9, 8.
Query 3 10: 출력 59 (9 + 4 + 5 + 9 + 7 + 8 + 9 + 8)
End :          끝나다
건축 수: 선분 나무의 장점 은 ★ 가능 한 것 과 미리 계산 하 는 것 이다 (예 를 들 어 결점 16, 17, 9, 10, 11 의 합 을 계산 하려 면 출력 결점 2 만 사용 하면 된다. 조회 할 때 추가 하 는 과정 을 줄 였 다)
                                                                     55(1)
                                                /                                                   \
                                 15(2)                                                               40(3)
                             /                    \                                                  /                           \
                   6(4)                           9(5)                        21(6)                        19(7)
                /              \                      /              \                       /                \                  /                  \
          3(8)           3(9)      4(10)      5(11)     13(12)     8(13)     9(14)        10(15)  
       /            \            【3】         【4】          【5】       /             \        【8】        【9】             【10】
1(16)     2(17)                                             6(18)        7(19)
【1】          【2】                                                 【6】             【7】
tip: () 중 결점 번호, [] 중 범위 아래 표 시 됩 니 다. 보통 입력 한 수치 와 일부 범위 내의 합 입 니 다.
모든 나무 결점:
#define ll rt<<1
#define rr rt<<1|1
using namespace std;
const int maxn=50010;
struct Tree{
	int l,r;//sum                 
	int sum;
}tree[maxn<<2];

① 라인 트 리 만 들 기:
void PushUP(int rt){
	tree[rt].sum= tree[ll].sum +tree[rr].sum;
}
void build(int l,int r,int rt)
{
	tree[rt].l=l; tree[rt].r=r;
	if(l==r){
		scanf("%d",&tree[rt].sum);
		return ;
	}
	int m=(l+r)>>1;
	build(l,m,ll);  //ll  -> #define ll rt<<1
	build(m+1,r,rr);//rr  -> #define rr rt<<1|1
	PushUP(rt);
}

트 리 를 만 드 는 범위 의 가장 왼쪽 값 (기본 값 은 1 부터), 가장 오른쪽 값 (입력 한 n), 트 리 의 총 뿌리 결산 점 (기본 값 은 1 을 총 뿌리 결산 점) build (1, n, 1) 을 입력 하 십시오.
tree[ rt ].l = l ;  tree[ rt ].r = r ; 
전체 루트 노드 rt = 1 (1).그러면 tree [1] 의 가장 왼쪽 은 1 이 고 가장 오른쪽 은 n 이다. 즉, 결점 (1) 에 포 함 된 것 과 1 에서 n 까지 의 것 이다.
그리고 각각 좌우 양쪽 으로 나 누 어 나 무 를 지 었 다.
build( l , m , rt * 2 );
왼쪽 (1, (n + 1) / 2 )
그러면 왼쪽 뿌리 노드 는 rt = 2 * rt = 2 (2) 。그러면 tree [2] 의 가장 왼쪽 은 1 이 고 가장 오른쪽 은 (n + 1) / 2 이다.
계속 좌우 로 나 무 를...
build( m+1 , r , rt * 2+1 );
오른쪽
PushUP( rt );
위의 두 단 계 를 실행 한 후에 뿌리 노드 rt 는 좌우 노드 가 있 습 니 다. 그러면 rt 의 값 은 rt * 2 와 rt * 2 + 1 의 합 입 니 다.
if ( l == r )
l = = r 까지 범 위 를 한 점 으로 축소 한 다 는 뜻 입 니 다. 그러면 이 점 에서 값 을 입력 하 십시오.
★ 왜 나무 밑바닥 의 잎 사 귀 노드 가 1, 2, 6 인가 ,7 이 아니 라 1, 2, 3, 4?
나 무 를 만 드 는 과정 이 반 으로 접 혀 있 기 때문에 배열 [10] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.
반, 반, 하나, 둘, 셋, 45, 반, 67, 8910 으로 나 뉜 다.
그리고 계속 양쪽 으로 나 눕 니 다. 끝까지 나 눌 때 1, 2 를 맨 밑 에 놓 고 위로 올 립 니 다.
(공교롭게도) 반반, 12345 와 678910 은 같은 효과 가 있 기 때문에 가장 밑바닥 잎 노드 의 값 은 1, 2, 6, 7 이다.
★ 좌우 에 나 무 를 세 우 는 순 서 를 바 꿀 수 있 나 요?
아니요. 입력 한 순 서 는 왼쪽 에서 오른쪽으로 하기 때문에 먼저 왼쪽으로 탐색 하고 반 으로 접어 서 왼쪽 밑 으로 탐색 하 며 첫 번 째 수 를 입력 한 다음 에 끝까지 탐색 하고 다른 수 를 순서대로 입력 해 야 합 니 다. 이렇게 하면 순서대로 나무 에 들 어 가 는 것 입 니 다.
좌우 로 나 무 를 만 드 는 순서 가 바 뀌 면 입력 한 수 는 원래 의 순서 가 아니다.
★ 수치 가 비교적 많 습 니 다. 여러 번 도와 주세요. 세 가지 유형 으로 나 눌 수 있 습 니 다.
(1) rt 이 값 은 노드 번호 입 니 다.
(2) tree [rt]. sum 이 값 은 입력 한 값 과 입력 한 값 의 부분 과
(3) tree [rt]. l, tree [rt]. r 의 값 은 좌우 범위, 즉 1 ~ n 의 수 이다.
② 업데이트 작업
void update(int p,int add,int rt)
{
	if( tree[rt].l == tree[rt].r ) {
		tree[rt].sum += add;
		return ;
	}
	int m = ( tree[rt].l + tree[rt].r ) >>1;
	if(p <= m) update( p, add, ll );
	else update( p, add, rr );
	PushUP(rt);
}

입력: 변경 할 값 의 위치 p, 추가 할 값 add, 총 루트 노드 rt (기본 1) 루트 노드 rt 의 좌우 범위 값 이 같 을 때 이 조회 점 입 니 다. 루트 노드 이 값 + add
업데이트 할 값 이 rt 의 좌우 범위 의 중간 값 보다 작다 면, 업데이트 할 위 치 는 rt 의 왼쪽 에 있 고, 반대로 오른쪽 에 있 습 니 다.
위의 업 데 이 트 를 마 친 후에 값 을 다시 계산 합 니 다.
③ 조회 조작
int query(int L,int R,int rt)
{
	if(L <= tree[rt].l && tree[rt].r <= R)  
		return tree[rt].sum;
	int m = ( tree[rt].l + tree[rt].r )>>1;
	if(R <= m) return query(L,R,ll);
	else if(L > m) return query(L,R,rr);
	else return query(L,R,ll) + query(L,R,rr);
} 

입력: 검색 할 좌우 범위, L ~ R (L, R 포함) 및 총 루트 노드 rt
만약 에 찾 으 려 는 루트 노드 범위 L R 에 마침 결점 rt 왼쪽 에서 오른쪽으로 포함 되 어 있다 면 다행 입 니 다. 결점 rt 왼쪽 에서 오른쪽 까지 의 값 sum 은 이미 계산 되 었 기 때문에 더 이상 찾 지 않 아 도 됩 니 다. 바로 이것 과 같 습 니 다.
                                          m
     ( tree[ rt ].l ) |_______|_______|( tree[ rt ].r)
                (L)|_____________________|(R)
         [상태 1 (왼쪽 과 오른쪽 모두 안에 포함 되 지 않 은 것 이 조금 남 았 습 니 다. 아래 의 다른 상태 일 수도 있 습 니 다. 그리고 상태 4 로 통합 합 니 다)]
그리고 보면 R 이 m 보다 작 으 면...
                                                                   m
                           ( tree[ rt ].l ) |________|________|( tree[ rt ].r)
                     ( tree[ rt * 2 ].l ) |________|( tree[ rt * 2 ].r )
      (L)|_____________________|(R)
         [상태 2: 왼쪽으로 범 위 를 좁 히 기 (이 그림 은 오른쪽 부분 만 고려 하고 왼쪽 부분 은 다른 상태 일 수 있 습 니 다. 그리고 상태 4 로 통합)] L 이 m 보다 크 면
                                     m
    ( tree[ rt ].l ) |______|______|( tree[ rt ].r)
      ( tree[ rt * 2 +1 ].l ) |______|( tree[ rt * 2 +1 ].r)
                                       (L)|_____________________|(R)
         [상태 3: 오른쪽으로 범 위 를 축소 합 니 다 (이 그림 은 왼쪽 부분 만 고려 하고 오른쪽 부분 은 다른 상태 일 수 있 습 니 다. 그리고 상태 4 로 통합 합 니 다)] 다른 상황:
                                                        m                                                                                             m
    ( tree[ rt ].l ) |______________|_____________|( tree[ rt ].r)    ( tree[ rt ].l ) |________|________|( tree[ rt ].r)
                            |______________||_____________|                                                  |________||________|
                            (L)|_____________________|(R)                                          (L)|_____________________|(R)                           【 상태 4: 좌우 추가 가 필요 합 니 다. 】                                                              [상태 4 의 하위 상태 (그리고 오른쪽 은 안 그 릴 게 요)]
다른 상태 가 있 냐 는 질문 이 있 을 수 있 습 니 다.
                                     m
    ( tree[ rt ].l ) |______|______|( tree[ rt ].r)
                                                       (L) | | (R) ★ 그렇다면 [상태 3] 과 같 지 않 나 요?
답: 이 부분 은 존재 하지 않 습 니 다. 우 리 는 이 부분 을 고려 하지 않 습 니 다. 처음에 범위 가 가장 큰 1 ~ n 이 었 을 것 입 니 다. 모든 상황 을 포함 하고 있 었 기 때 문 입 니 다. 그러면 [상태 4] 는 우리 가 원 하 는 부분 으로 계속 축소 되 었 습 니 다.
이런 상황 이 있 지만 이 상황 은 바로 [상태 2] 와 [상태 3] 가 버 린 부분 입 니 다. 두 번 째 줄 을 보 세 요. 우리 의 절반 조작 은 이미 버 리 고 원 하지 않 습 니 다.
④ 주 함수:
int main()
{
	int cas=1,T,n;  
	scanf("%d",&T);  
	while(T--)  
	{  
		printf("Case %d:
",cas++); scanf("%d",&n); build(1,n,1); /* for(int i=1;i<=2*n;i++){ printf("%d %d %d
",tree[i].l , tree[i].sum , tree[i].r); } for(int i=1;i<=3*n;i++) printf("%d--",sum[i]);*/ char op[10]; while(scanf("%s",op)){ if(op[0]=='E') // break; int a,b; scanf("%d%d",&a,&b); if(op[0]=='Q') // printf("%d
",query(a,b,1)); else if(op[0]=='S') // update(a,-b,1); else if(op[0]=='A') // update(a,b,1); } } return 0; }

좋은 웹페이지 즐겨찾기