codeforces #285 div2 D(康托,二分)
2015-01-12 22:17:45
思路:精简的好题。
将两个排列的康托展开式的因子列出来,然后保存在数组中,相加的话满足大数加法+过程取模。这里因为数太多,所以需要用树状数组来维护比某个数小的数已经出现多少个。
具体过程就是从第一个数开始,看它比多少个未出现的数大(这个值存数组),处理完两个排列后加起来。之后就是逆康托展开,用二分来搜索当前数该是几。
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <iostream> 11 #include <algorithm> 12 using namespace std; 13 #define lp (p << 1) 14 #define rp (p << 1|1) 15 #define getmid(l,r) (l + (r - l) / 2) 16 #define MP(a,b) make_pair(a,b) 17 typedef long long ll; 18 typedef unsigned long long ull; 19 typedef pair<int,int> pii; 20 const int INF = (1 << 30) - 1; 21 const int maxn = 200010; 22 23 ll n; 24 ll cof[maxn]; 25 26 struct BIT{ 27 ll c[maxn]; 28 void clear(){ memset(c,0,sizeof(c));} 29 ll Lowbit(ll x){ return x & -x;} 30 void Update(ll x,ll d){ 31 while(x <= 200000){ 32 c[x] += d; 33 x += Lowbit(x); 34 } 35 } 36 ll Getsum(ll x){ 37 ll res = 0; 38 while(x){ 39 res += c[x]; 40 x -= Lowbit(x); 41 } 42 return res; 43 } 44 }T; 45 46 ll Solve(ll val){ 47 ll cur,mid,l = 1,r = n; 48 while(l < r){ 49 mid = getmid(l,r); 50 cur = T.Getsum(mid); 51 if(cur >= val) r = mid; 52 else l = mid + 1; 53 } 54 T.Update(l,-1); 55 return l - 1; 56 } 57 58 int main(){ 59 ll t; 60 scanf("%I64d",&n); 61 T.clear(); 62 for(ll i = 1; i <= n; ++i){ 63 scanf("%I64d",&t); 64 cof[i] = t - T.Getsum(t + 1); //Cantor cof 65 T.Update(t + 1,1); 66 } 67 T.clear(); 68 for(ll i = 1; i <= n; ++i){ 69 scanf("%I64d",&t); 70 cof[i] += t - T.Getsum(t + 1); 71 T.Update(t + 1,1); 72 } 73 for(ll i = n; i >= 1; --i){ 74 cof[i - 1] += cof[i] / (n - i + 1); 75 cof[i] %= n - i + 1; 76 } 77 for(ll i = 1; i <= n; ++i){ 78 if(i > 1) printf(" "); 79 printf("%I64d",Solve(cof[i] + 1)); 80 } 81 puts(""); 82 return 0; 83 }