수열 블록 입문 1 ~ 9 문제 풀이

블록 을 나 누 는 것 이 좋 습 니 다!
개술
블록 을 나 누 는 것 은 수열 을 여러 개의 블록 으로 나 누 어 각각 블록 안의 정 보 를 유지 하 는 것 이다. 조 회 는 각 블록 안의 정 보 를 호출 하면 매우 유연 한 데이터 구조 이다.
우선 변수 정의:
l e n len 각 블록의 크기 n u m num num 블록의 수량 b l [i] bl [i] bl [i] i 번 요 소 는 몇 번 블록 l [i], r [i] l [i], r [i] l [i], l [i], r [i] 각 블록의 좌우 단점 에 속한다.
평균 값 부등식 에 따라 우 리 는 n \ sqrt n 개의 블록 을 만 드 는 것 이 가장 좋 고 모든 블록 크기 도 n \ sqrt n 이다.
어떻게 블록 을 만 들 수 있 습 니까?
void build() {
	len = sqrt(n), num = (n - 1) / len + 1 ;
	rep(i, 1, n) bl[i] = (i - 1) / len + 1 ;
	rep(i, 1, num) l[i] = (i - 1) * len + 1, r[i] = i * len ;
	r[num] = n ; //   
}

코드 는 비교적 분명 하 다. 유일 하 게 주의해 야 할 것 은 마지막 조각 이 이전 보다 작 을 것 이다. 왜냐하면 그의 오른쪽 단점 은 반드시 n n 이기 때문이다.
그리고 저 희 는 L. O. J. LOJ 의 9999 문 제 를 통 해 P. r. o. b. l. e. m 를 분석 해 보도 록 하 겠 습 니 다.  S e t Problem \ Set Problem Set
예제 1
n n n 으로 긴 수열 과 n n n 개의 조작 을 제시 하고 구간 덧셈, 단일 검사 값 을 조작 합 니 다.
가장 기본 적 인 문제 부터.
우 리 는 t a g [i] tag [i] tag [i] 로 i i 블록 증가 값 을 기록 합 니 다.
수정 작업 [x, y] [x, y] [x, y] [x, y]
만약 x x x 와 y y y 가 같은 덩어리 에 속한다 면 a [x..... y] a [x... y] a [x... y] a [x... y] 를 직접 폭력 적 으로 수정 합 니 다.
그렇지 않 으 면 전체 블록 에 대해 t a g tag 를 직접 수정 하고 다른 분산 요소 에 대해 a [i] a [i] a [i] a [i] a [i] 를 직접 폭력 적 으로 수정 합 니 다.
검색 의 x x x 에 대한 답 은 a [x] + t a g [b l [x] a [x] + tag [bl [x] a [x] + tag [bl [x]]]] 이다.
시간 복잡 도 O  n ) O(n~\sqrt n) O(n n ​)
const int N = 100010 ;
const int M = 320 ;

int bl[N], l[M], r[M], tag[M], a[N] ;
int n, len, num ;
/* bl[i] : i          
 * l[i]  i    
   r[i]  i    
   tag[i] i        
 */

void build() {
	len = sqrt(n), num = (n - 1) / len + 1 ;
	rep(i, 1, n) bl[i] = (i - 1) / len + 1 ;
	rep(i, 1, num) l[i] = (i - 1) * len + 1, r[i] = i * len ;
	r[num] = n ;
}

void change(int x, int y, int val) {
	if (bl[x] == bl[y]) rep(i, x, y) a[i] += val ; //     ,    
	else {
		rep(i, bl[x] + 1, bl[y] - 1) tag[i] += val ; //     
		rep(i, x, r[bl[x]]) a[i] += val ; //     
		rep(i, l[bl[y]], y) a[i] += val ;
	}
}

int query(int x) {
	return a[x] + tag[bl[x]] ; // O(1)  
}

signed main(){
    scanf("%d", &n) ; int m = n ;
	rep(i, 1, n) scanf("%d", &a[i]) ;
	build() ;
	while (m--) {
		int op ; scanf("%d", &op) ;
		if (op == 0) {
			int x, y, val ; scanf("%d%d%d", &x, &y, &val) ;
			change(x, y, val) ;
		} else {
			int x, y, val ; scanf("%d%d%d", &x, &y, &val) ;
			printf("%d
"
, query(y)) ; } } return 0 ; }

이 문제 의 연습 을 통 해 우 리 는 블록 버스터 의 작업 원 리 를 대충 요약 할 수 있다. 블록 버스터 처리, 작은 폭력 이다.
예제 2
n n n 으로 긴 수열 과 n n n 개의 조작 을 제시 하고 구간 덧셈 과 관련 되 며 구간 내 에서 특정한 값 x x x 보다 작은 요소 개 수 를 문의 합 니 다.
우선 당신 은 조작 을 수정 하지 않 고 어떻게 하 는 지 알 아야 합 니 다.
  • 분산 작업 에 대해 직접 폭력 검색
  • 전체 블록 에 대해 정렬 후 2 점 찾기
  • 수정 작업 도 여전히 간단 하 다.
  • 전체 블록 에 대해 우 리 는 상대 적 인 순서 가 바 뀌 지 않 기 때문에 다시 정렬 할 필요 가 없다 는 것 을 알 게 되 었 다. 2 분 의 수 를 v - t a g [i] v - tag [i] v - tag [i] 로 바 꾸 면 된다. 시간 복잡 도 O (n) O (\ sqrt n) O (n)
  • 분산 요소 에 대해 어 쩔 수 없 이 폭력 적 으로 수정 하여 정렬 할 수 밖 에 없다. 시간 복잡 도 O (n)  l o g   n ) O(\sqrt n \ log \ n) O(n ​ log n)

  • 분석 총 시간 복잡 도 O (n)  l o g   n ) O(n \sqrt n \ log \ n) O(nn ​ log n)
    int n, m, len, num ;
    int a[N], bl[N], l[M], r[M], tag[M] ;
    vi v[M] ;
    
    void build() {
    	len = sqrt(n), num = (n - 1) / len + 1 ;
    	rep(i, 1, n) {
    		bl[i] = (i - 1) / len + 1 ;
    		v[bl[i]].pb(a[i]) ;
    	}
    	rep(i, 1, num) {
    		l[i] = (i - 1) * len + 1 ;
    		r[i] = i * len ;
    		sort(v[i].begin(), v[i].end()) ;
    	}
    	r[num] = n ;
    }
    
    void reset(int x) { //   x  
    	v[x].clear() ;
    	rep(i, l[x], r[x]) v[x].pb(a[i]) ;
    	sort(v[x].begin(), v[x].end()) ;
    }
    
    void change(int x, int y, int val) {
    	if (bl[x] == bl[y]) { //     ,  
    		rep(i, x, y) a[i] += val ;
    		reset(bl[x]) ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) tag[i] += val ;
    		rep(i, x, r[bl[x]]) a[i] += val ;
    		rep(i, l[bl[y]], y) a[i] += val ;
    		reset(bl[x]) ; reset(bl[y]) ; //     
    	}
    }
    
    int query(int x, int y, int val) {
    	int ans = 0 ;
    	if (bl[x] == bl[y]) { //     ,    
    		rep(i, x, y) if (a[i] + tag[bl[i]] < val) ans++ ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1)
    		ans += lower_bound(v[i].begin(), v[i].end(), val - tag[i]) - v[i].begin() ; //     
    		rep(i, x, r[bl[x]]) if (a[i] + tag[bl[i]] < val) ans++ ; //     
    		rep(i, l[bl[y]], y) if (a[i] + tag[bl[i]] < val) ans++ ;
    	}
    	return ans ;
    }
    
    signed main(){
        scanf("%d", &n) ; int m = n ;
    	rep(i, 1, n) scanf("%d", &a[i]) ;
    	build() ;
        while (m--) {
        	int op, x, y, val ;
    		scanf("%d%d%d%d", &op, &x, &y, &val) ;
    		if (!op) change(x, y, val) ;
    		else printf("%d
    "
    , query(x, y, val * val)) ; } return 0 ; }

    예제 3
    n n 의 시퀀스 와 n n n 의 길 이 를 지정 합 니 다.구간 덧셈 지원, 구간 조회 v v v 의 전구.
    이것 은 앞의 저것 과 차이 가 많 지 않 으 니, 단지 2 점 만 수정 하면 된다. 독자 들 은 직접 코드 를 볼 수 있다.
    int n, m, len, num ;
    int a[N], bl[N], l[M], r[M], tag[M] ;
    vi v[M] ;
    
    void build() {
    	len = sqrt(n), num = (n - 1) / len + 1 ;
    	rep(i, 1, n) {
    		bl[i] = (i - 1) / len + 1 ;
    		v[bl[i]].pb(a[i]) ;
    	}
    	rep(i, 1, num) {
    		l[i] = (i - 1) * len - 1, r[i] = i * len ;
    		sort(v[i].begin(), v[i].end()) ;
    	}
    	r[num] = n ;
    }
    
    void reset(int x) {
    	v[x].clear() ;
    	rep(i, l[x], r[x]) v[x].pb(a[i]) ;
    	sort(v[x].begin(), v[x].end()) ;
    }
    
    void change(int x, int y, int val) {
    	if (bl[x] == bl[y]) {
    		rep(i, x, y) a[i] += val ;
    		reset(bl[x]) ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) tag[i] += val ;
    		rep(i, x, r[bl[x]]) a[i] += val ;
    		rep(i, l[bl[y]], y) a[i] += val ;
    		reset(bl[x]) ; reset(bl[y]) ;
    	}
    }
    
    int query(int x, int y, int val) {
    	int ans = -1 ;
    	if (bl[x] == bl[y]) {
    		rep(i, x, y) if (a[i] + tag[bl[i]] < val) ans = max(ans, a[i] + tag[bl[i]]) ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) {
    			vector <int>::iterator it = lower_bound(v[i].begin(), v[i].end(), val - tag[i]) ;
    			if (it != v[i].begin()) ans = max(ans, *(--it) + tag[i]) ;
    		}
    		rep(i, x, r[bl[x]]) if (a[i] + tag[bl[i]] < val) ans = max(ans, a[i] + tag[bl[i]]) ;
    		rep(i, l[bl[y]], y) if (a[i] + tag[bl[i]] < val) ans = max(ans, a[i] + tag[bl[i]]) ;
    	}
    	return ans ;
    }
    
    signed main(){
    	scanf("%d", &n) ; int m = n ;
    	rep(i, 1, n) scanf("%d", &a[i]) ;
    	build() ;
    	while (m--) {
    		int op, x, y, val ;
    		scanf("%d%d%d%d", &op, &x, &y, &val) ;
    		if (!op) change(x, y, val) ;
    		else printf("%d
    "
    , query(x, y, val)) ; } return 0 ; }

    예제 4
    n n 의 시퀀스 와 n n n 의 길 이 를 지정 합 니 다.구간 덧셈, 구간 구 화 를 지원 합 니 다.정 답 은 0 9 + 7 10 ^ 9 + 7 109 + 7 모델 링.
    구간 구 화?t a g tag tag 조회 만으로 O (n) O (\ sqrt n) O (n) 가 됩 니 다.
    우 리 는 s u m [i] sum [i] sum [i] 을 정의 합 니 다.
    우 리 는 조작 수정 을 고려 하고 있다.
  • 분산 요소 에 대해 a [i] a [i] a [i] a [i] 와 t a g [b l [i] tag [bl [i]] tag [bl [i]]]]]
  • 를 직접 폭력 적 으로 수정 합 니 다.
  • 전체 블록 에 대해 t a g [i] tag [i] tag [i] tag [i] 를 수정 하면 조회 작업 에 대해:
  • 분산 요소 에 대해 우 리 는 a [i] + t a g [b l [i] a [i] + tag [bl [i]] a [i] + tag [bl [i]] 로 정 답 을 업데이트 합 니 다
  • 전체, 정 답 은 s u m [i] + t a g [i] ∗ l e n sum [i] + tag [i] * len sum [i] + tag [i] ∗ len
  • 시간 복잡 도 O (n) O (n \ sqrt n) O (n)
    int n, m, len, num ;
    ll a[N], bl[N], l[M], r[M], tag[M], sum[M] ;
    
    void build() {
    	len = sqrt(n) ; num = (n - 1) / len + 1 ;
    	rep(i, 1, n) {
    		bl[i] = (i - 1) / len + 1 ;
    		sum[bl[i]] += a[i] ;
    	}
    	rep(i, 1, num) {
    		l[i] = (i - 1) * len + 1 ;
    		r[i] = i * len ;
    	}
    	r[num] = n ;
    }
    
    void change(int x, int y, int val) {
    	if (bl[x] == bl[y]) {
    		rep(i, x, y) a[i] += val ;
    		sum[bl[x]] += 1ll * (y - x + 1) * val ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) tag[i] += val ;
    		rep(i, x, r[bl[x]]) a[i] += val ;
    		sum[bl[x]] += 1ll * (r[bl[x]] - x + 1) * val ;
    		rep(i, l[bl[y]], y) a[i] += val ;
    		sum[bl[y]] += 1ll * (y - l[bl[y]] + 1) * val ;
    	}
    }
    
    ll query(int x, int y, int MOD) {
    	int ans = 0 ;
    	if (bl[x] == bl[y]) {
    		rep(i, x, y) ans = (ans + a[i]) % MOD ;
    		ans = (ans + 1ll * (y - x + 1) * tag[bl[x]] % MOD) % MOD ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) {
    			ans = (ans + sum[i]) % MOD ;
    			ans = (ans + 1ll * tag[i] * len % MOD) % MOD ;
    		}
    		rep(i, x, r[bl[x]]) ans = (ans + a[i]) % MOD ;
    		ans = (ans + 1ll * (r[bl[x]] - x + 1) * tag[bl[x]] % MOD) % MOD ;
    		rep(i, l[bl[y]], y) ans = (ans + a[i]) % MOD ;
    		ans = (ans + 1ll * (y - l[bl[y]] + 1) * tag[bl[y]] % MOD) % MOD ;
    	}
    	return ans ;
    }
    
    signed main(){
    	scanf("%d", &n) ; m = n ;
    	rep(i, 1, n) scanf("%lld", &a[i]) ;
    	build() ;
    	while (m--) {
    		int op, l, r, val ; scanf("%d%d%d%d", &op, &l, &r, &val) ;
    		if (!op) change(l, r, val) ;
    		else printf("%lld
    "
    , query(l, r, val + 1)) ; } return 0 ; }

    예제 5
    n n 의 시퀀스 와 n n n 의 길 이 를 지정 합 니 다.구간 개방, 구간 구 화 를 지원 합 니 다.
    이 문제 도 매우 체계 적 이어서 '화신 이 각국 을 여행 하 다' 고 한 사람 은 모두 이 방법 을 알 것 이다.
    우 리 는 1, 0, 9, 10, 9, 109 의 수 를 발 견 했 는데, 최대 55 제곱 을 열 면 1, 1, 1 이 된다.
    모든 구간 이 1 인 구간 에 대해 개방 작업 을 하 는 것 은 분명 의미 가 없다.
    그러면 우 리 는 그 블록 들 을 기록 할 수 있 고 개방 적 인 조작 도 할 수 있 으 며 폭력 으로 끝 날 것 이다.
    시간 복잡 도 O (n) O (n \ sqrt n) O (n)
    int n, m, len, num ;
    int a[N], bl[N], l[M], r[M], flg[M], sum[M] ;
    
    void build() {
    	len = sqrt(n) ; num = (n - 1) / len + 1 ;
    	rep(i, 1, n) bl[i] = (i - 1) / len + 1, sum[bl[i]] += a[i] ;
    	rep(i, 1, num) l[i] = (i - 1) * len + 1, r[i] = i * len ;
    	r[num] = n ;
    }
    
    void modify(int x, int y) {
    	if (bl[x] == bl[y]) {
    		rep(i, x, y) {
    			sum[bl[i]] -= a[i] ;
    			a[i] = sqrt(a[i]) ;
    			sum[bl[i]] += a[i] ;
    		}
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) {
    			if (flg[i]) continue ;
    			sum[i] = 0, flg[i] = 1 ;
    			rep(j, l[i], r[i]) {
    				a[j] = sqrt(a[j]) ;
    				sum[i] += a[j] ;
    				flg[i] &= (a[j] <= 1) ;
    			}
    		}
    		rep(i, x, r[bl[x]]) {
    			sum[bl[i]] -= a[i] ;
    			a[i] = sqrt(a[i]) ;
    			sum[bl[i]] += a[i] ;
    		}
    		rep(i, l[bl[y]], y) {
    			sum[bl[i]] -= a[i] ;
    			a[i] = sqrt(a[i]) ;
    			sum[bl[i]] += a[i] ;
    		}
    	}
    }
    
    int query(int x, int y) {
    	int ans = 0 ;
    	if (bl[x] == bl[y]) {
    		rep(i, x, y) ans += a[i] ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) ans += sum[i] ;
    		rep(i, x, r[bl[x]]) ans += a[i] ;
    		rep(i, l[bl[y]], y) ans += a[i] ;
    	}
    	return ans ;
    }
    
    signed main(){
    	scanf("%d", &n) ; m = n ;
    	rep(i, 1, n) scanf("%d", &a[i]) ;
    	build() ;
    	while (m--) {
    		int op, x, y, val ;
    		scanf("%d%d%d%d", &op, &x, &y, &val) ;
    		if (op == 0) modify(x, y) ;
    		else printf("%d
    "
    , query(x, y)) ; } return 0 ; }

    예제
    n n n 으로 긴 수열 과 n n n 개의 조작 을 보 여 줍 니 다. 조작 은 한 점 삽입, 한 점 문의, 데이터 무 작위 생 성 과 관련 됩 니 다.
    다음 알고리즘 은 무 작위 생 성 에 대응 할 수 있 습 니 다.
    무 작위 로 생 성 된 데이터 에 대해 서 는 블록 마다 삽입 횟수 가 균등 하 게 분담 되 어 있 습 니 다. 우 리 는 하나의 v e c t o r vector vector 를 통 해 만 해결 할 수 있 습 니 다.
    그러나 극한 데이터 에서 블록 에 n n 회 이상 삽입 하면 블록 이 매우 큰 상황 을 초래 할 수 있 습 니 다.
    그러면 우 리 는 시간의 복잡 도 를 유지 하기 위해 블록 을 재 구성 할 수 있 습 니 다. 블록 크기 가 20 * 8727 ° n 20 * \ sqrt n 20 * 8727 ° n 에 이 르 렀 을 때 서열 을 재 구성 할 수 있 습 니 다.
    총 시간 복잡 도 O (n) O (n \ sqrt n) O (n)
    int n, m, len, num ;
    int a[N], st[N << 1], bl[M] ;
    vi v[M] ;
    
    void build() {
    	len = sqrt(n), num = (n - 1) / len + 1 ;
    	rep(i, 1, n) bl[i] = (i - 1) / len + 1, v[bl[i]].pb(a[i]) ;
    }
    
    void cg() { //   
    	int top = 0 ;
    	rep(i, 1, num) {
    		rep(j, 0, siz(v[i]) - 1) st[++top] = v[i][j] ;
    		v[i].clear() ;
    	}
    	len = sqrt(top), num = (top - 1) / len + 1 ;
    	rep(i, 1, top) bl[i] = (i - 1) / len + 1, v[bl[i]].pb(st[i]) ;
    }
    
    pii get(int k) { //    x           
    	int x = 1 ;
    	while (k > siz(v[x])) k -= siz(v[x]), x++ ;
    	return mp(x, k - 1) ;
    }
    
    void change(int x, int y) {
    	pii r = get(x) ;
    	int i = r.fi ;
    	v[i].insert(v[i].begin() + r.se, y) ; //    vector  
    	if (siz(v[i]) > 20 * len) cg() ;
    }
    
    int query(int x) {
    	pii r = get(x) ;
    	return v[r.fi][r.se] ;
    }
    
    signed main(){
        scanf("%d", &n) ; m = n ;
        rep(i, 1, n) scanf("%d", &a[i]) ;
        build() ;
    	while (m--) {
    		int op, x, y, val ; scanf("%d%d%d%d", &op, &x, &y, &val) ;
    		if (!op) change(x, y) ;
    		else printf("%d
    "
    , query(y)) ; } return 0 ; }

    예제
    n n n 으로 긴 수열 과 n n n 개의 조작 을 제시 하고 구간 곱셈, 구간 덧셈, 단점 질문 과 관련된다.
    또 하나의 틀 에 박 힌 문제 이다.
    선분 나무 로 이 문 제 를 어떻게 풀 었 습 니까?두 개의 배열 m u l [i], a d [i] mul [i], add [i] mul [i], add [i] 를 열 고 유지 합 니 다.
    그러면 블록 도 비슷 해 요.
    우선 강제 곱셈 이 덧셈 보다 우선이다
    각 블록 에 대해 a d [i], m u l [i] add [i], mul [i] add [i], mul [i] add [i], mul [i] 로 각각 전체 블록 내 더하기 와 곱 하기 값 을 표시 합 니 다.
    하나의 요소 에 대해 a [i] a [i] a [i] 의 실제 값 은 a [i] * 8727 ° m u l [b l [i] + a d [b l [i] a [i]] * mul [bl [i]] + add [bl [i]] a [i]] * 8727 ° mul [bl [i]]] + add [bl [i]]]]]]
    곱셈 수정 작업 에 대해 a d [i] add [i] add [i] 와 m u l [i] mul [i] mul [i] 는 각각 a d [i] ∗ v a l add [i] * val add [i] ∗ val 과 m u l [i] ∗ v a l mul [i] * val mul [i] ∗
    덧셈 조작 에 대해 a d [i] add [i] add [i] 와 m u l [i] mul [i] mul [i] 를 각각 a d [i] + v a l add [i] + val add [i] + val 과 m u l [i] mul [i] mul [i] mul [i] mul [i] 로 표시 합 니 다.
    분산 요소 에 대해 서 는 직접 폭력 전 투 를 통 해 비 워 두 고 그 자 체 를 수정 하면 된다.
    int n, m, len, num ;
    int a[N], bl[N], l[M], r[M], add[M], mul[M] ;
    
    void build() {
    	len = sqrt(n), num = (n - 1) / len + 1 ;
    	rep(i, 1, n) bl[i] = (i - 1) / len + 1 ;
    	rep(i, 1, num) l[i] = (i - 1) * len + 1, r[i] = i * len, mul[i] = 1 ;
    	r[num] = n ;
    }
    
    void reset(int x) {
    	rep(i, l[x], r[x]) a[i] = (a[i] * mul[x] + add[x]) % MOD ;
    	add[x] = 0, mul[x] = 1 ;
    }
    
    void Add(int x, int y, int val) {
    	if (bl[x] == bl[y]) {
    		reset(bl[x]) ;
    		rep(i, x, y) a[i] = (a[i] + val) % MOD ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) add[i] = (add[i] + val) % MOD ;
    		reset(bl[x]) ; reset(bl[y]) ;
    		rep(i, x, r[bl[x]]) a[i] = (a[i] + val) % MOD ;
    		rep(i, l[bl[y]], y) a[i] = (a[i] + val) % MOD ;
    	}
    }
    
    void Mul(int x, int y, int val) {
    	if (bl[x] == bl[y]) {
    		reset(bl[x]) ;
    		rep(i, x, y) a[i] = a[i] * val % MOD ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) {
    			add[i] = add[i] * val % MOD ;
    			mul[i] = mul[i] * val % MOD ;
    		}
    		reset(bl[x]) ; reset(bl[y]) ;
    		rep(i, x, r[bl[x]]) a[i] = a[i] * val % MOD ;
    		rep(i, l[bl[y]], y) a[i] = a[i] * val % MOD ;
    	}
    }
    
    int query(int x) {
    	return (a[x] * mul[bl[x]] + add[bl[x]]) % MOD ;
    }
    
    signed main(){
    	scanf("%d", &n) ; m = n ;
    	rep(i, 1, n) scanf("%d", &a[i]) ;
    	build() ;
    	while (m--) {
    		int op, l, r, val ; scanf("%d%d%d%d", &op, &l, &r, &val) ;
    		if (op == 0) Add(l, r, val % MOD) ;
    		else if (op == 1) Mul(l, r, val % MOD) ;
    		else printf("%d
    "
    , query(r)) ; } return 0 ; }

    예제
    n n n 으로 긴 수열 과 n n n 개 동작 을 보 여 줍 니 다. 구간 문의 와 같은 수 c c c 의 요 소 를 조작 하고 이 구간 의 모든 요 소 를 c c c c 로 바 꿉 니 다.
    우 리 는 폭력 적 인 방법 을 고려 했다.
    수정 작업
  • 분산 요소 에 대해 우 리 는 폭력 적 으로 수정 했다
  • 전체 블록 에 대해 블록 에 표 시 를 하면 모두 v a l val
  • 이다.
    문의 조작
  • 분산 요소 에 대해 우 리 는 폭력 조회
  • 를 내 려 놓 았 다.
  • 전체 블록 에 대해 v a l val 로 표시 되 어 있 으 면 답 에 l e n len 의 공헌 이 있 습 니 다. 그렇지 않 으 면 폭력 조회
  • 시간 복잡 도 O (n 2) O (n ^ 2) O (n2)?
    자세히 분석:
    초기 서열 이 모두 같은 값 이 라 고 가정 하면 단어 조회 의 시간 복잡 도 는 O (n) O (\ sqrt n) O (n) 이다.
    구간 조작 이 라면 가장 많은 파괴 링 1 위 가 222 인 블록 의 표 시 를 통 해 우 리 는 두 개의 폭력 시간 을 증가 시 켰 다. 평균 분담 시간 은 여전히 O (n) O (\ sqrt n) O (n) 이다.
    쉽게 말 하면 출제 자 는 O (n) O (n) O (n) 의 시간 을 한 번 써 서 대답 하 기 를 원한 다. 그 는 O (n) O (\ sqrt n) O (n) 의 수정 작업 을 써 야 한다.
    그래서 평균 분담 시간 복잡 도 O (n) O (n \ sqrt n) O (N)
    int n, m, len, num ;
    int a[N], bl[N], l[M], r[M], cov[M], tag[M] ;
    
    void build() {
    	len = sqrt(n), num = (n - 1) / len + 1 ;
    	rep(i, 1, n) bl[i] = (i - 1) / len + 1 ;
    	rep(i, 1, num) l[i] = (i - 1) * len + 1, r[i] = i * len ;
    	r[num] = n ;
    }
    
    void reset(int x) {
    	if (!tag[x]) return ;
    	rep(i, l[x], r[x]) a[i] = cov[x] ;
    	tag[x] = 0 ;
    }
    
    void modify(int x, int y, int val) {
    	if (bl[x] == bl[y]) {
    		reset(bl[x]) ;
    		rep(i, x, y) a[i] = val ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1) tag[i] = 1, cov[i] = val ;
    		reset(bl[x]) ; reset(bl[y]) ;
    		rep(i, x, r[bl[x]]) a[i] = val ;
    		rep(i, l[bl[y]], y) a[i] = val ;
    	}
    }
    
    int query(int x, int y, int val) {
    	int ans = 0 ;
    	if (bl[x] == bl[y]) {
    		reset(bl[x]) ;
    		rep(i, x, y) ans += (a[i] == val) ;
    	} else {
    		rep(i, bl[x] + 1, bl[y] - 1)
    		if (tag[i]) ans += (cov[i] == val ? len : 0) ;
    		else {
    			rep(j, l[i], r[i]) ans += (a[j] == val) ;
    		}
    		reset(bl[x]) ; reset(bl[y]) ;
    		rep(i, x, r[bl[x]]) ans += (a[i] == val) ;
    		rep(i, l[bl[y]], y) ans += (a[i] == val) ;
    	}
    	return ans ;
    }
    
    
    signed main(){
    	scanf("%d", &n) ; m = n ;
    	rep(i, 1, n) scanf("%d", &a[i]) ;
    	build() ;
    	while (m--) {
    		int x, y, val ; scanf("%d%d%d", &x, &y, &val) ;
    		printf("%d
    "
    , query(x, y, val)) ; modify(x, y, val) ; } return 0 ; }

    예제
    n n n 으로 긴 수열 과 n n n 개의 조작 을 제시 하고 질문 구간 과 관련 된 최소 중 수 를 조작 합 니 다.
    이것 은 조금 어렵 습 니 다. 우 리 는 먼저 조작 을 수정 하지 않 은 문 제 를 고려 합 시다.
    먼저 m o d e (i) mode (i) mode (i) 를 i i 개의 전체 블록 으로 정의 합 니 다.
    우 리 는 하나의 성질 을 발견 했다. 하나의 질문 에 대해 [x, y] [x, y] [x, y], 답 은 8712 ° m o d e (i \ in mode (i * 8712 ° mode (i ~ j)  ∪ j)~ \cup j) 분산 원소
    이 성질 은 분명 하 다.
    x x x 는 블록 a a a, y y 는 블록 b b 에 속 합 니 다.
    그러면 [x, y] [x, y] [x, y] 는 다음 과 같이 나 눌 수 있다.
  • x x x 에서 블록 a a 까지 의 꼬리 요소
  • 블록 a + 1 a + 1 a + 1 에서 블록 b - 1 b - 1 b - 1
  • 블록 b b 의 첫 번 째 요소 에서 y y
  • 까지
    이 성질 에 따라 우 리 는 n \ sqrt n 개 수의 출현 횟수 만 비교 하면 된다.
    우 리 는 O (n) O (n \ sqrt n) O (N) 를 예 처리 할 수 있다.
    질문 에 대해 [x, y] [x, y] [x, y]
  • 분산 요소 에 대해 우 리 는 폭력 적 으로 모든 요소 가 [x, y] [x, y] [x, y] 내 에 나타 난 횟수 를 찾 을 수 있다. 이 과정 은 a [i] a [i] a [i] a [i] 의 등장 위 치 를 기록 하 는 v [a [i] v [a]] v [a [i]]] 를 통 해 2 점 검색
  • 을 할 수 있다.
  • 전체 블록 에 대해 중 수 는 f [b l [x] + 1] [b l [y] - 1] f [bl [x] + 1] [bl [y] - 1] f [bl [x] + 1] [bl [y] - 1]
  • 우 리 는 이 O (n) O (\ sqrt n) O (n) 개수 에서 가장 많은 횟수 를 찾 으 면 된다 는 것 을 알 게 되 었 다.
    계산 을 편리 하 게 하기 위해 서 우 리 는 데이터 에 대해 이산 화 해 야 한다.
    시간 복잡 도 O  l o g   n ) O(n \sqrt n ~log~n) O(nn ​ log n)
    int n, m, len, num ;
    int a[N], tmp[N], bl[N], l[M], r[M], cnt[N], f[M][M] ;
    vi p[N] ;
    
    void build() {
    	rep(i, 1, n) tmp[i] = a[i] ;
    	sort(tmp + 1, tmp + n + 1) ;
    	int sz = unique(tmp + 1, tmp + n + 1) - tmp - 1 ;
    	rep(i, 1, n) {
    		a[i] = lower_bound(tmp + 1, tmp + sz + 1, a[i]) - tmp ;
    		p[a[i]].pb(i) ;
    	}
    	len = sqrt(n / log2(n)), num = (n - 1) / len + 1 ;
    	rep(i, 1, n) bl[i] = (i - 1) / len + 1 ;
    	rep(i, 1, num) l[i] = (i - 1) * len + 1, r[i] = i * len ;
    	r[num] = n ;
    }
    
    void init() {
    	rep(i, 1, num) {
    		clr(cnt) ;
    		int ans = 0, mx = 0 ;
    		rep(j, i, num) {
    			rep(k, l[j], r[j]) {
    				cnt[a[k]]++ ;
    				if (cnt[a[k]] > mx || (cnt[a[k]] == mx && a[k] < ans)) ans = a[k], mx = cnt[a[k]] ;
    			}
    			f[i][j] = ans ;
    		}
    	}
    }
    
    int sum(int l, int r, int val) {
    	return upper_bound(p[val].begin(), p[val].end(), r) -
    	lower_bound(p[val].begin(), p[val].end(), l) ;
    }
    
    void solve(int x, int y, int l, int r, int &ans, int &mx) {
        rep(i, x, y) {
        	int now = sum(l, r, a[i]) ;
        	if (now > mx || (now == mx && a[i] < ans)) ans = a[i], mx = now ;
    	}
    }
    
    int query(int x, int y) {
    	int ans = 0, mx = 0 ;
    	if (bl[x] + 1 >= bl[y]) solve(x, y, x, y, ans, mx) ;
    	else {
    		ans = f[bl[x] + 1][bl[y] - 1], mx = sum(x, y, ans) ;
    		solve(x, r[bl[x]], x, y, ans, mx) ;
    		solve(l[bl[y]], y, x, y, ans, mx) ;
    	}
    	return ans ;
    }
    
    signed main(){
    	scanf("%d", &n) ; m = n ;
    	rep(i, 1, n) scanf("%d", &a[i]) ;
    	build() ; init() ;
    	while (m--) {
    		int x, y ; scanf("%d%d", &x, &y) ;
    		printf("%d
    "
    , tmp[query(x, y)]) ; } return 0 ; }

    감사합니다

    좋은 웹페이지 즐겨찾기