线段数+逆序对
在学习了著名的数据结构线段树之后,急切地想找几个题来写写,于是随便找了一题,谁知道一调就是一个晚上,终究还是太菜了。
P1966 [NOIP2013 提高组] 火柴排队
题目中要我们求经过多少次变换之后使得火柴之间的距离最短。
很容易我们就可以知道,当第一列火柴的第k长的火柴和第二列火柴的第k长的火柴在一起的时候距离最短。
那么现在问题就变成了怎么样经过最少的变换让第一列的第k长的火柴和第二列第k长的火柴对上。
由于数据比较庞大,而且我们只在意火柴长度的排名,所以使用离散化。
我们创建一个结构体,里面存着火柴的长度和火柴目前所在的位置,然后我们对结构体数组按火柴长度进行排序。
之后我们用一个数组q[100010],q[i]的i表示另一个数组需要到达的位置,q[i]则对应它当前的位置,具体怎么实现看代码吧。
然后想要把火柴从当前位置移动到目标位置的步数实际上是逆序对的个数。
线段树求逆序对,就用节点储存当前范围已经插入的数,然后查找的时候查找比要查找的数大的范围 即x+1---n即可
#include<iostream> #include<algorithm> int mod = 1e8 - 3; using namespace std; int q[1000010], ls1[1000010], ls2[1000010]; long long ans = 0, all = 0; struct node { long long l, r, data; }tree[5000010]; struct s { int data, site;//data表示值,site表示位置 }a[1000010], b[1000010]; bool cmp(s x, s y) { return x.data < y.data; } void update(int i) { tree[i].data = (tree[i << 1].data + tree[i << 1 | 1].data); } int search(int i, int l,int r) { if (tree[i].l>=l&&tree[i].r<=r) { return tree[i].data; } if (tree[i].l > r || tree[i].r < l)return 0; long long now = 0; if (tree[i << 1].r >= l) now +=search(i << 1, l, r); if (tree[i << 1 | 1].l <= r) now += search(i << 1 | 1, l, r); return now; } void build(int i, int l, int r) { tree[i].l = l; tree[i].r = r; if (l == r) { return; } int mid = (l + r) / 2; build(i << 1, l, mid); build(i << 1 | 1, mid + 1, r); } void change(int i, int k,int l,int r) { tree[i].l = l; tree[i].r = r; if (l == r) { tree[i].data++; return; } int mid = (l + r) / 2; if (tree[i << 1].r >= k)change(i << 1, k, l, mid); if (tree[i << 1 | 1].l <= k)change(i << 1 | 1, k, mid + 1, r); update(i); } int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i].data; a[i].site = i; } for (int i = 1; i <= n; i++) { cin >> b[i].data; b[i].site = i; } sort(a + 1, a + 1 + n, cmp); sort(b + 1, b + 1 + n, cmp); for (int i = 1; i <= n; i++) { q[a[i].site] = b[i].site;//使q[b中第i个数要到的位置]=b中第i个数目前的位置 } build(1, 1, n); for (int i = 1; i <= n; i++) { ans += search(1, q[i] + 1, n); ans %= mod; change(1, q[i], 1, n); } cout << ans; return 0; }

浙公网安备 33010602011771号