킥 시작 2019 라운드 G 문제 C는이 개 솔루션을 이동 (SOS DP, 세그먼트 트리)

첫 번째 용액 : A와 B는 각각 만족 값의 조합에 대해 만족 산출 N 시프트 값의 모든 조합에 대해> = H가 상기 강모와 SETB에 저장된다. i가 나타내는 상기 I-SETB OR 작업에서 발견 될 수있는 각각의 세타 열거 조합 (1 << N), J -1 어떤 조합하는 I (A)의 조합 : 행복 값> = H, B의 조합 J : 행복 값> = H 및 A 및 B를 만족 N 시프트의 조합 듀티상의 적어도 하나의 사용자를 갖는다. (원리의 이용 여기서, I는 경우 | J은 = (1 << N) - 1이면 J 슈퍼 세트 K, 만족 I이다 | (1 << N) -1 = K)와, SOS DP에 의해 계산된다 슈퍼 세트의 수의 조합.

주 : 이차원 배열 NUM DP 여기서 2 차원의 이해를 용이하게하기 위해, 1 차원 배열에 대한 재귀 관계를 최적화한다.

더 자세한 기사가 SOS DP, 소개 : HTTP : //codeforces.com/blog/entry/45223

차이점은 문서가 존재 슈퍼 세트 (상위 집합)을 찾는의 조합이고, 상기 문서가 부분 집합 (서브 세트)의 조합 (SET)을 찾는 도입된다는 것이다. 수퍼 또는 본질적인 사상의 일부이든 같다 : 예 101,010,111 들어 네번째 비트는 0이고,이 조합의 상위 4는 0 또는 1 일 수있다 비트, 3 비트, 다음의 1 조합 상위 3 호는 단지 하나 일 수있다. (달리 서브 세트가이 조합의 서브 세트 만 4 비트 0, 비트 3이고, 부분 집합 일 수있다 0 또는 1).

"난 상이한 비트 전에 같은 하이 레벨"NUM [I] [마스크] 부분 집합의 개수 조합 마스크이다.

(1 개)  수입 java.util.Scanner;
2  가져 오기 는 java.util.HashSet;
3  공용  클래스 GCJ_KI2019G_C5 {   // SOS DP 
4      공공  정적  무효 메인 (문자열 []에 args) {
 5          = 스캐너에서 새로운 스캐너 (System.in);
6          INT T = in.nextInt ();        
7           ( INT에서 , CAS <= T; CAS = 1 CAS ++ ) {
 8              INT N = in.nextInt ();
9              길이 H = in.nextLong ();
10              [] A = 새로운  [N];
11               [] B = 새로운  [N];
12               스마 = 0L;    sumB = 0L ;
(13)              에 대한이 ( int로 0 = 1을, 난 <N; i가 ++ ) {
 14                  A [I] = in.nextLong ();
15                  스마 +가 = A [I];
16              }
 17               ( int로 ; i가 N <I는 I = 0 ++ ) {
 18 개                  (B) [I] = in.nextLong ();
19                  sumB + = B의 [I];
(20)              }
 (21)              의 경우 (스마 <H sumB || < H) {
 22                  에서 System.out.println (CAS + + "# 케이스" ":"+0 );
(23)                  계속 ;
24              }
 25  
26              HashSet에 <정수> = 세타 새로운 HashSet의 <> ();
27              RECUR (a, 0, H, 0L 0 세타);
28              HashSet에 <정수> = SETB 새로운 HashSet의 <> ();
29              RECUR (b, 0, H, 0L 0 , SETB); 
30  
(31)              INT [] [] NUM = 새로운  INT [N + 1] [1 <<엔];
(32)              INT (X) = (1 << N) - 1 ;
33               ( INT의 V : SETB)
 34                  NUM [0] [V] = 1 ; 
35              
(36)              에 대해 ( int로 , I가 N = <; I = 1 난 ++) {                  // SOS DP 
37                   ( INT의 마스크 = 0; 마스크 <=의 X, 마스크 ++ ) {
 38                      NUM [I] [마스크 = NUM [I- 1 ] [마스크];
39                      의 경우 ((및 마스크 (1 << (I-1))) == 0 )
 40                          NUM [I] [마스크] + = NUM [I-1] [마스크 | << 1 (I-1 )];
41                  }
42              }
 43                      
44               ANS = 0리터 ;            
45               ( INT의 V : 세타) {
 46                  ANS NUM = + [N] [X- V];  
47              }        
 48              에서 System.out.println ( "케이스 #"CAS + + ""+ ANS);
49          }
 50      }
 51      
52      정적  무효 RECUR ( 길이 [] 도착, INT의 P,  시간,  ACCU, INT의 비트 마스크 HashSet에 <정수> 집합) {    
 53          하다면(p == arr.length) {                  
 54              의 경우 (ACCU> = H) { 
 55                set.add (비트 마스크);
56              }
 57              ;
58          }
 59          RECUR (도착, P + 1 , H, ACCU, 비트 마스크 세트);
60          RECUR (도착, P + 1, H, ACCU 도착 + [P], 비트 마스크 | (1 개 << (P))을 설정);
61      }
 62 }

******************************************* 분할 선 ***** ******************************************

제 2 용액 :

(N)의 변화는 2 개 개의 섹션 0 ~ N / 2 N / 2 + 1 내지 N-1로 분할

각 부분을 복수 형성하는 제 단락 행복 값 쌍에 대해 만족 값, 두 번째 단락 행복 값 쌍이 존재한다면 pair1.first + pair2.first> = H +와 pair1.second pair2.second> 충족하는 것 = H는,이 유효한 솔루션이다.

한 쌍의 제 단락, 분할 트리를 사용하여 신속하게 상기 조건을 만족하는 한 쌍의 두 번째 단락의 개수를 알 수있다.

수입 java.util.Scanner; 
수입 인 java.util.ArrayList; 
수입 Collections의; 

공용 클래스 해결 { 
    공공 정적 무효 메인 (문자열 []에 args) { 
        = 새로운 스캐너 (System.in)의 스캐너; 
		T = INT in.nextInt ();	
		대 (INT 캐스 = 1; CAS <= T; CAS ++) { 
			INT N = in.nextInt (); 
			H = INT in.nextInt (); 
			긴 [] A = 새로운 긴 [N]; 
			긴 [] B = 새로운 긴 [N]; 
			긴 스마 = 0L; 긴 sumB = 0L; 
			{위해 (; i가 N <I는 I = 0 ++ INT) 
				이 [I] = in.nextLong ()를; 
				스마 +는 A = [I]; 
			} 
			에 대해 INT (I = 0; I <N은, 내가 ++) { 
				B [I] = in.nextLong (); 
				sumB + = B의 [I]; 
			}
			경우 (스마 <H sumB || <H) { 
				에서 System.out.println ( "케이스 #"CAS + + ""+0); 
				계속하다; 
			} 
			
			ArrayList를 <쌍>리스트 1 = 새로운 ArrayList를 <> (); 
			재발 (A, B, 0, N / 2,0L, 0L, 목록 1, 1H); 
			ArrayList를 <쌍>리스트 2 = 새로운 ArrayList를 <> (); 
			재발 (A, B, N / 2 + 1, N-1,0L, 0L,리스트 2, H); 
			은, Collections.sort (리스트 1); 
			은, Collections.sort (리스트 2); 
			SegTree 나무 = 새로운 SegTree (0 시간); 
			ANS의 길이 = 0;			
			INT list2.size p = () - 1; 
			(쌍 쌍 :리스트 1)에 대한 { 
				반면 (p> = 0 && list2.get (p) .fir> = H - pair.fir) { 
					tree.add (list2.get (p) .sec); 
					피--; 
				} 
				ANS + = tree.get (H - pair.sec, 1H);
			에서 System.out.println ( "사례 #"+ CAS + ":"+ ANS);				
		}는 
	} 
		} 
		공공 INT의 GET (A, INT의 B를 int)를 {
	
		INT 낮은 고; 
		SegTree는 오른쪽에서 왼쪽; 
		INT 합 = 0; 
		공개 SegTree (INT의 B, A INT) { 
			낮은 = A; 높은 = B를; 
		} 
		공개 무효가 (INT의 V) {추가 
			금액을 ++; 
			(저 == 높음) 경우 
				리턴; 
			INT의 m = / 2 (고저) 로우 +; 
			경우 (V <= m) { 
				경우 (왼쪽 == NULL) 
					= 새로운 SegTree (저, m) 왼쪽; 
				left.add (V); 
			} 
			다른 { 
				경우 (오른쪽 == NULL) 
					오른쪽 새로운 SegTree = (m + 1, 높이); 
				right.add (V); 
			} 

			(a <= 낮은 &&의 B> = 높음) 경우 
				환원 액; 
			경우 (A> B의 높은 || <낮음) 
				복귀 0; 
			INT 입술 = 0;
			경우 (왼쪽 = NULL!) 
				입술 left.get = (a, b); 
			경우 (오른쪽 = NULL!) 
				입술 right.get + = (a, b); 
			고해상도를 반환; 
		} 
	} 
	정적 클래스 쌍 필적 <쌍> {구현 
		INT 전나무, 초; 
		공공 쌍 (INT의 B, A INT) { 
			= 전나무; 초 = B를; 
		} 
		공개 INT은 compareTo (쌍 쌍) { 
			Integer.compare (FIR, pair.fir)을 반환; 
		} 
	} 
 
	정적 무효 RECUR를 (길이 [] 길고 [] B, INT의 P, INT 단부 긴 accuA 긴 accuB, ArrayList를 <쌍>에서, INT 높이) { 
		경우 (p> 종료) {
			에는 list.add (새로운 쌍 ((INT) Math.min (H, accuA) (INT) Math.min (H, accuB))); 
			반환; 
		} 
		재발 (A, B, P + 1 단부 accuA이 + A [P] accuB 목록, 1H); 
		재발 (A, B, P + 1 단부 accuA, accuB + B [P],리스트, 1H); 
		재발 (A, B, P + 1 단부 accuA는 + A [P]는 accuB + B [P],리스트, 1H); 
	}
}

  

 

추천

출처www.cnblogs.com/black-fish/p/11756428.html