첫 번째 용액 : 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); } }