codeforces 540E"Infinite Inversions"
题意:
给你一个无限大的整数序列 p = {1, 2, 3, ...};
有 n 次操作,每次操作交换第 ai 个数和第 aj 个数;
求序列中逆序对的个数;
题解:
考虑交换完后的序列,存在连续的区间 [ i , j ] 使得 p[ i ] = i , 那么分下一下这种区间的特点
假设 i 之前有 x 个数大于 p[ i ],那么可知对于第 i 个数有 x 个逆序对,同理,因为 p[ i+1 ] = p[ i ]
所以 i+1 之前也一定有且只有 x 个数大于 p[ i+1 ],那么第 i+1 个数也有 x 个逆序对,那么对于连续的区间[i,j]
易的区间 [ i , j ] 与其之前的数可构成的逆序对的个数为 (j-i+1)*x 个,那么可将其映射为一个值 i ,并记录其有 (j-i+1) 个数;
例如,假设 n 次操作为
1 4
1 8
那么交换完后,前 8 个数的排列状况为:
1 2 3 4 5 6 7 8
8 2 3 1 5 6 7 4
那么可得区间 [2,3] 和区间[5,6]的值未发生改变,当然区间[9,+∞]也为发生改变,但[9,+∞]并不影响答案,所以不用考虑。
那么根据之前提到的,将为改变的大区间映射到某个值身上,并记录有多少数映射到这个值上了,如下所示:
1 2 3 4 5
8 2 1 5 4
映射完后,区间[2,3]合并到了2上,区间[5,7]合并到了5上,接下来就是求映射完后的数组的逆序对总个数,这就变成了经典的
树状数组求逆序对个数的题了。
这就完事了么????当然不是~~~
考虑 4 这个值,其之前的 5 只给 4 提供了一个逆序对,但实际上 6,7 也会提供,那么这就少算了两个逆序对数,这该怎么办呢?
考虑某个区间 [i , j],一共有 j-i+1 个数,易得区间 [ i , j ] 只会影响其之后且值小于 i 的数,那么就再用一次树状数组,如果来到了某个
映射的值,那么将 i 之后的数通过树状数组 +(j-i),对于某个在其之后且值小于 i 的数 x ,直接在答案上额外加上 Sum(x+1)即可。
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<map> 5 #include<algorithm> 6 using namespace std; 7 #define lowbit(x) (x&-x) 8 #define ll long long 9 #define mem(a,b) memset(a,b,sizeof(a)) 10 const int maxn=1e6+50; 11 12 int n; 13 map<int,int>f;//记录交换后的序列状况 14 int diff[maxn]; 15 int sum[maxn]; 16 struct Date 17 { 18 int val; 19 int id;//初始编号 20 int tot;//如果某个区间映射到值val上,那么tot记录的就是区间的总个数 21 int newVal;//离散后的值 22 Date(int val=0,int id=0,int tot=0):val(val),id(id),tot(tot){} 23 }_date[maxn]; 24 25 struct BIT 26 { 27 int bit[maxn]; 28 void Init() 29 { 30 mem(bit,0); 31 } 32 void Add(int t,int x,int k) 33 { 34 while(t <= k) 35 { 36 bit[t] += x; 37 t += lowbit(t); 38 } 39 } 40 int Sum(int t) 41 { 42 int sum=0; 43 while(t > 0) 44 { 45 sum += bit[t]; 46 t -= lowbit(t); 47 } 48 return sum; 49 } 50 }_bit; 51 52 bool cmp1(Date a,Date b) 53 { 54 return a.val < b.val; 55 } 56 bool cmp2(Date a,Date b) 57 { 58 return a.id < b.id; 59 } 60 61 int Trans() 62 { 63 map<int,int>::iterator it; 64 int index=0; 65 int preIndex=f.begin()->first; 66 int nexIndex; 67 68 for(it=++f.begin();it != f.end();++it) 69 { 70 _date[++index]=Date(f[preIndex],index,1); 71 nexIndex=it->first; 72 73 //判断是否为未改变的区间 74 int tot=nexIndex-preIndex-1; 75 if(tot >= 1) 76 _date[++index]=Date(preIndex+1,index,tot); 77 78 preIndex=nexIndex; 79 } 80 _date[++index]=Date(f[preIndex],index,1); 81 82 sort(_date+1,_date+index+1,cmp1);//按其val由小到大排序,方便离散化 83 for(int i=1;i <= index;++i) 84 _date[i].newVal=i;//离散化 85 sort(_date+1,_date+index+1,cmp2);//按 id 从小到大复原 86 87 return index; 88 } 89 90 ll Solve() 91 { 92 //离散化 93 int index=Trans(); 94 _bit.Init();//树状数组初始化 95 ll ans=0; 96 97 //经典树状数组求逆序对个数 98 for(int i=1;i <= index;++i) 99 { 100 int x=_date[i].newVal; 101 _bit.Add(x,1,index); 102 ans += 1ll*(i-_bit.Sum(x))*_date[i].tot; 103 } 104 105 //求解区间少加的逆序对个数 106 _bit.Init(); 107 for(int i=1;i <= index;++i) 108 { 109 int x=_date[i].newVal; 110 _bit.Add(x,_date[i].tot-1,index);//只有 tot > 1才会加入到树状数组中 111 112 //求解 i 前值 > x 的未改变的区间少加的数的总个数 113 if(_date[i].tot == 1) 114 ans += _bit.Sum(index)-_bit.Sum(x); 115 116 } 117 118 return ans; 119 } 120 int main() 121 { 122 // freopen("C:/Users/hyacinthLJP/Desktop/stdin/contest","r",stdin); 123 scanf("%d",&n); 124 for(int i=1;i <= n;++i) 125 { 126 int a,b; 127 scanf("%d%d",&a,&b); 128 int x,y; 129 x=(!f.count(a) ? a:f[a]); 130 y=(!f.count(b) ? b:f[b]); 131 132 f[a]=y; 133 f[b]=x; 134 } 135 printf("%I64d\n",Solve()); 136 137 return 0; 138 }