티스토리 뷰
[CF 911D] Inversion Counting - 1700
문제 설명 :
초기의 1~n으로 이루어진 permutation이 주어진다. 그리고 m개의 쿼리마다 [l,r]범위에 포함되는 인덱스를 reverse한다. 이 때 매 쿼리마다 어떤 임의의 인덱스 i,j에 대해서 i<j면서 a_i<a_j를 만족시키는 서로 다른 i,j의 pair가 짝수개인지 홀수개인지 구하는 문제이다. 매 쿼리는 이전 쿼리들에 의해 누적된 permutation에 대해서 시행한다.
풀이 :
n=1500, 쿼리(m)는 20만이다. O(nm)은 3억이므로 잘 하면 풀 수도 있겠지만 정해는 아닐거라 생각해서 어떻게든 O(m*logn) 혹은 O(m*1)으로 푸는 방법이 있을 것이라고 생각했다. 오랜 시간을 투자해서 여러가지 방법을 계속 생각해봤는데, 매 쿼리는 종속적이다보니 짝수,홀수인지는 어떻게 구간 배열로 판명한다고 쳐도, update하는게 불가능해서 실패했다.
이 문제의 정답은 결론부터 애기하고 이후에 증명하는게 나을 것 같다. 결론은 매 쿼리의 구간의 크기(=r-l+1)을 seg라고 둔다면, seg*(seg-1)/2의 값이 짝수라면 이전 state와 동일하고, 홀수라면 이전 state와 반대의 결과가 나온다. 왜 그런지 살펴보자.
문제에서 매 쿼리[l,r]마다 reverse 연산을 수행하게 되는데 쿼리에 포함되지 않는 바깥쪽 영역들은 이전 permutation의 counting과 결과가 동일하다. 다시 말해 [1,r-1], [l+1,n]의 카운팅 결과는 이전 상태와 동일하다. 따라서 내가 고려해야 할 것은 오직 [l,r]의 카운팅 결과가 어떻게 되는지이다. 그런데 여기서 또 하나의 규칙을 발견할 수 있다. 구간의 크기를 seg라고 했을 때, (reverse 시행 전 [l,r]의 카운팅) + (reverse 시행 후 [l,r]카운팅) = seg*(seg-1)/2 가 반드시 성립한다는 것이다. 각 수를 그래프의 한 node라고 생각하고 counting조건을 부합한다는 것을 i->j로 가는 directed edge를 그리는 것으로 생각해보면 reverse연산을 수행한다는 것은 그래프에서 모든 edge들을 뒤집는다는 뜻이다. 처음의 edge와 뒤집어진 edge의 합집합은 노드의 개수가 n개인 그래프에서 그을 수 있는 모든 edge의 경우의 수이므로 n*(n-1)/2 개 이다.
여기까지 증명을 완료했고 문제를 다시 정리해보면, 결국 내가 고려해야 할 것은 [l,r] 구간의 카운팅이 이전 permutation의 홀짝과 동일하느냐이다. 이를 판단하기 위한 단서가 바로 시행 전 카운팅과 시행 후 카운팅이 반드시 seg*(seg-1)/2인 것이다. 그럼 이제 이 공식을 어떻게 적용해야 할 것인가? 정답은 바로 seg*(seg-1)/2 (=편의상 L로 두자)가 짝수인지 홀수인지로 판별할 수 있다. L이 만약 짝수라면 현재 카운팅은 이전의 카운팅과 동일할 것이고, L이 홀수라면 현재 카운팅은 이전 카운팅과 반대일 것이다.
식으로 적어보면 count_prev + count_cur = L(=짝수) 라면 count_prev == count_cur가 성립하고,
count_prev + count_cur = L(홀수) 라면 count_prev + count_cur =1 (서로 반대라는 뜻)이 성립한다.
주의할 점 :
-
배운 점 :
아이디어성 문제였다.
코드 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #include<stdio.h> #include<math.h> #include<string.h> #include<iostream> #include<functional> #include<string> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<assert.h> #include<stdlib.h> #include<stack> using namespace std; /*********************Contest Template***********************/ typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<long long,long long> pll; #define FASTIO ios_base::sync_with_stdio(false);cin.tie(0); struct S{ int a,b; S(){}S(int _a,int _b) { a=_a; b=_b; } const bool operator<(const S &o) const{ return a<o.a;} }; priority_queue<int,vector<int>,greater<int>> mpq; const int SPACE=0,NL=1; string exm; inline void showAll(vector<int> &v,int sep){ //0=space,1="\n" for(int &here:v) printf("%d%c",here,(sep)?'\n':' '); } inline void exf(void){ cout<<exm<<"\n"; exit(0); } /*********************Contest Template***********************/ const int SIZE= 1509; int arr[SIZE]; int main(){ int n; scanf("%d",&n); int cnt=0; for(int i=1 ; i<=n ; i++){ scanf("%d",&arr[i]); } for(int i=1 ; i<=n ; i++){ for(int j=i+1 ; j<=n ; j++){ if(arr[i]>arr[j]) cnt^=1; } } int m; scanf("%d",&m); while(m--){ int l,r; scanf("%d %d",&l,&r); int seg=r-l+1; if((seg*(seg-1)/2)%2==1) cnt^=1; printf("%s\n",(cnt)?"odd":"even"); } return 0; } /* */ | cs |
'Problem Solving > Math' 카테고리의 다른 글
[CF 1194D] 1-2-K Game (0) | 2019.09.28 |
---|---|
[CF 1186C] Vus the Cossack and Strings (0) | 2019.08.10 |
[Codeforces 1159B] Expansion coefficient of the array (0) | 2019.05.13 |