线段树求解区间内乱序求逆序数及其转化变形问题
HDU 1394 Minimum Inversion Number
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意:
给定一个区间,并将区间内数字乱序排列,每次操作可以将乱序数组的第一个放置到数组的最后,问通过操作能形成的最小逆序数是多少。
(在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为4 5 1 3 2, 那么这个序列的逆序数为7,逆序对分别为(4, 1), (4, 3), (4, 2), (5, 1), (5, 3), (5, 2),(3, 2)。)
思路:
通过模拟上述操作可以发现,每次把第一个数放到最后,逆序数的的变化量应该是(-a[i]+n-(a[i]+1)),即sum=sum-a[i]+n-(a[i]+1);
1.暴力
遍历一次乱序数组,把当前状态下的逆序数求出来,然后进行n次题目所给操作,将第一个数放置到最后求逆序数,取最小值即可。
2.线段树
初始建树,使每个点的num值都初始化为0,然后从数组末尾进行询问更新,询问是否当前存在多少个比自身小的数,然后更新当前点。每次询问结果的总和即为数组当前状态下的逆序数。然后同理遍历操作取最小值。
AC代码
暴力代码就不放了,下面是线段树:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#include<sstream>
#include<sstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define debug() puts("what the fuck!")
#define dedebug() puts("what the fuck!!!")
#define ll long long
#define ull unsigned long long
#define speed {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
using namespace std;
const double PI = acos(-1.0);
const int maxn = 2e6 + 10;
const int N = 5e3 + 10;
const ll INF = 1e18;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps_0 = 1e-9;
const double gold = (1 + sqrt(5)) / 2;
template<typename T>
inline void rd(T& x) {
int f = 1; x = 0; char c = getchar();
while (c<'0' || c>'9') { f = -1; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
x *= f;
}
template<typename T>
inline T gcd(T a, T b) {
return b ? gcd(b, a % b) : a;
}
int n;
int ans = 0;
int a[N];
struct node {
int l, r;
int num;
}tree[maxn<<2];
void push_up(int i) {
tree[i].num = tree[i << 1].num + tree[i << 1 | 1].num;
}
void build(int i, int l, int r) {
tree[i].l = l;
tree[i].r = r;
tree[i].num = 0;
if (l == r)return;
int mid = (l + r) >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
push_up(i);
}
void query(int i, int l, int r) {
if (tree[i].l == tree[i].r) {
ans += tree[i].num;
return;
}
int mid = (tree[i].l + tree[i].r) >> 1;
if (l > mid)query(i << 1 | 1, l, r);
else if (r <= mid)query(i << 1, l, r);
else {
query(i << 1, l, mid);
query(i << 1 | 1, mid + 1, r);
}
}
void update(int i, int dis) {
if (tree[i].l == tree[i].r && tree[i].l == dis) {
tree[i].num++;
return;
}
int mid = (tree[i].l + tree[i].r) >> 1;
if (dis <= mid) {
update(i << 1, dis);
}
else {
update(i << 1 | 1, dis);
}
push_up(i);
}
signed main() {
while (~scanf("%d", &n)) {
build(1, 1, n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
int sum = 0;
for (int i = n; i >= 1; --i) {
ans = 0;
int dis = a[i] + 1;
if (dis != 1)query(1, 1, dis);//询问比dis小的数存在多少个
sum += ans;
update(1,dis);
}
//cout << sum << " " << tree[1].num << endl;
int ans = sum;
for (int i = 1; i <= n; ++i) {
sum = sum - a[i] + n - (a[i] + 1);
ans = min(ans, sum);
}
printf("%d\n", ans);
}
return 0;
}
POJ 2481 Cows
题目链接http://poj.org/problem?id=2481
题意:
给定n个区间,若一个区间a有一个子集区间b,则说明a区间比b区间强壮,问每个区间在给定的所有区间中,有几个区间比自己强壮。
思路:
(想了好久,就是不知道如何转化到线段树上!!!! )
设区间左边界为st,右边界为ed,根据st升序排序,ed降序排序,那么就可以保证按序遍历时,前面的区间比后面的区间要更长,那么就相当于在当前区间,强壮区间只可能存在于当前区间前面,那么
就相当于时我们固定了左端点st,我们只需要询问更新右端点ed,即判断在当前右端点ed前面有多上个比ed大的值,所得个数即为当前区间的强壮区间个数。那么就和求逆序数差不多的操作,区间询问,单点修改。
AC代码
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<cctype> #include<iomanip> #include<map> #include<vector> #include<list> #include<deque> #include<stack> #include<queue> #include<set> #include<cctype> #include<string> #include<stdexcept> #include<fstream> #include<sstream> #include<sstream> #define mem(a,b) memset(a,b,sizeof(a)) #define debug() puts("what the fuck!") #define dedebug() puts("what the fuck!!!") #define ll long long #define ull unsigned long long #define speed {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); }; using namespace std; const double PI = acos(-1.0); const int maxn = 2e6 + 10; const int N = 5e3 + 10; const ll INF = 1e18; const ll mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const double eps_0 = 1e-9; const double gold = (1 + sqrt(5)) / 2; template<typename T> inline void rd(T& x) { int f = 1; x = 0; char c = getchar(); while (c<'0' || c>'9') { f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } x *= f; } template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; } int n; int ans[maxn]; struct cow { int st, ed; int id; bool operator<(const cow& a)const { if (st == a.st)return ed > a.ed; return st < a.st; } }a[maxn]; struct node { int l, r; int sum; }tree[maxn << 2]; void push_up(int i) { tree[i].sum = tree[i << 1].sum + tree[i << 1 | 1].sum; } void build(int i, int l, int r) { tree[i].l = l; tree[i].r = r; if (l == r) { tree[i].sum = 0;//初始化为0 return; } int mid = (l + r) >> 1; build(i << 1, l, mid); build(i << 1 | 1, mid + 1, r); push_up(i); } int query(int i, int l, int r) { int res = 0; if (l <= tree[i].l && r >= tree[i].r)return tree[i].sum;//区间满足即可返回sum值!!! int mid = (tree[i].l + tree[i].r) >> 1; if (l <= mid)res += query(i << 1, l, r); if (r > mid)res += query(i << 1 | 1, l, r); return res; } void update(int i, int dis) { //cout << "i:" << i << " l:" << tree[i].l << " r:" << tree[i].r << " sum:" << tree[i].sum << endl; if (tree[i].l == tree[i].r) { tree[i].sum++; //cout << "i:" << i << " l:" << tree[i].l << " r:" << tree[i].r << " sum:" << tree[i].sum << endl; return; } int mid = (tree[i].l + tree[i].r) >> 1; if (dis <= mid)update(i << 1, dis); else update(i << 1 | 1, dis); push_up(i); } signed main() { while (scanf("%d", &n), n) { mem(ans, 0); for (int i = 1; i <= n; ++i) { scanf("%d%d", &a[i].st, &a[i].ed); a[i].st++; a[i].ed++; a[i].id = i; } build(1, 1, n); sort(a + 1, a + 1 + n); //for (int i = 1; i <= n; ++i) { // cout << a[i].id << " " << a[i].st << " " << a[i].ed << endl; //} for (int i = 1; i <= n; ++i) { if (i != 1 && a[i].st == a[i - 1].st && a[i].ed == a[i - 1].ed) { ans[a[i].id] = ans[a[i - 1].id];//如果区间相同,则可以直接得到答案 } else { ans[a[i].id] = query(1, a[i].ed, n); //cout << a[i].id << " " << ans[a[i].id] << endl; } update(1, a[i].ed); } for (int i = 1; i <= n; ++i) { printf("%d%c", ans[i], " \n"[i == n]); } } return 0; }
POJ 2182 Lost Cows
题目链接http://poj.org/problem?id=2182
题意:
n只牛用1-n来标号,接下来n-1行,分贝告诉你从第2到第n只牛左边有几个比它序号小的牛,求每个位置牛的编号
思路:
即求逆序数的变形,之前都是给定你数组,让你求逆序数,这次是把每个位置的逆序对个数告诉我们,让我们还原数组。
开一棵线段树,初始化num值为当前区间内含有的数字个数,然后我们从给定数组末尾去查询更新,每次更新我们查找到第a[i]小的数,返回值并减少一个数字量。
AC代码
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<cctype> #include<iomanip> #include<map> #include<vector> #include<list> #include<deque> #include<stack> #include<queue> #include<set> #include<cctype> #include<string> #include<stdexcept> #include<fstream> #include<sstream> #include<sstream> #define mem(a,b) memset(a,b,sizeof(a)) #define debug() puts("what the fuck!") #define dedebug() puts("what the fuck!!!") #define ll long long #define ull unsigned long long #define speed {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); }; using namespace std; const double PI = acos(-1.0); const int maxn = 2e6 + 10; const int N = 5e3 + 10; const ll INF = 1e18; const ll mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const double eps_0 = 1e-9; const double gold = (1 + sqrt(5)) / 2; template<typename T> inline void rd(T& x) { int f = 1; x = 0; char c = getchar(); while (c<'0' || c>'9') { f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } x *= f; } template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; } int n; int a[maxn]; int ans[maxn]; struct node { int l, r; int num; }tree[maxn << 2]; void build(int i, int l, int r) { tree[i].l = l; tree[i].r = r; tree[i].num = r - l + 1;//更新num值为区间内数字个数 //cout << i << " left:" << l << " right:" << r << " " << tree[i].num << "**" << endl; if (l == r)return; int mid = (l + r) >> 1; build(i << 1, l, mid); build(i << 1 | 1, mid + 1, r); } int query(int i, int dis) { tree[i].num--;//删除一个数字量 //cout << i << " left:" << tree[i].l << " right:" << tree[i].r << " " << tree[i].num << endl; if (tree[i].l == tree[i].r) { return tree[i].l;//查找到该值,返回. } if (tree[i << 1].num >= dis)return query(i << 1, dis); else return query(i << 1 | 1, dis - tree[i << 1].num); } signed main() { while (~scanf("%d", &n)) { a[1] = 1; for (int i = 2; i <= n; ++i) { scanf("%d", &a[i]); a[i]++; } build(1, 1, n); for (int i = n; i >= 1; --i) { ans[i] = query(1, a[i]); } for (int i = 1; i <= n; ++i) { printf("%d\n", ans[i]); } } return 0; }
POJ 2828 Buy Tickets
题目链接http://poj.org/problem?id=2828
题意:
一个长度为n的空数组,每次给你一个pos和val,将val插入到pos位置,最后输出该数组。
思路:
和上一题比较像,只是这次有了具体的其他值,依然可以按照上题的做法做,从后往前遍历,当前人可以排在i位置,那么说明它前面必然有i个空位置可以站,那么我们就从后往前依次询问更新,和上题一样,每次更新,位置就少了一个,注意询问返回值是数组中的下标,最后将val值与下标映射对应输出即可。
AC代码
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<cctype> #include<iomanip> #include<map> #include<vector> #include<list> #include<deque> #include<stack> #include<queue> #include<set> #include<cctype> #include<string> #include<stdexcept> #include<fstream> #include<sstream> #include<sstream> #define mem(a,b) memset(a,b,sizeof(a)) #define debug() puts("what the fuck!") #define dedebug() puts("what the fuck!!!") #define ll long long #define ull unsigned long long #define speed {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); }; using namespace std; const double PI = acos(-1.0); const int maxn = 2e6 + 10; const int N = 5e3 + 10; const ll INF = 1e18; const ll mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const double eps_0 = 1e-9; const double gold = (1 + sqrt(5)) / 2; template<typename T> inline void rd(T& x) { int f = 1; x = 0; char c = getchar(); while (c<'0' || c>'9') { f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } x *= f; } template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; } int n; int pos[maxn]; int val[maxn]; struct node { int l, r; int num; }tree[maxn << 2]; void build(int i, int l, int r) { tree[i].l = l; tree[i].r = r; tree[i].num = r - l + 1; if (l == r)return; int mid = (l + r) >> 1; build(i << 1, l, mid); build(i << 1 | 1, mid + 1, r); } int query(int i, int dis) { tree[i].num--; if (tree[i].l == tree[i].r)return tree[i].l; if (tree[i << 1].num >= dis)return query(i << 1, dis); else return query(i << 1 | 1, dis - tree[i << 1].num); } int ans[maxn]; signed main() { while (~scanf("%d", &n)) { mem(ans, 0); for (int i = 1; i <= n; ++i) { scanf("%d%d", &pos[i], &val[i]); } build(1, 1, n); for (int i = n; i >= 1; --i) { int p = query(1, pos[i] + 1); //cout << p << "**" << endl; ans[p] = val[i]; } for (int i = 1; i <= n; ++i) { printf("%d%c", ans[i], " \n"[i == n]); } } return 0; }

浙公网安备 33010602011771号