http://poj.org/problem?id=2828
Sample Input
4
0 77
1 51
1 33
2 69
4
0 20523
1 19243
1 3890
0 31492
Sample Output
77 33 69 51
31492 20523 3890 19243
题目大意,插队的问题,每个案例给出n,代表有n个插队的,每个给出p,v,意思是代号为v的人插在了第p个人的后面,问最后的队伍的排列?
题目中一开始整个队列是空的,也就是说如果输入i,j:代表代号为j的人插在了第i个人的后面,也就是说在他之前一定有了i个人,而他的位置是i+1。
所以由后向前推到,每个遇到的都是确定位置的,最后的人选定的位置不会改变,同样因为是倒叙输入,在第i个人后插队,也就是说他的前面一定要留下i个空格。
code:
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 210000
struct node {
int v, p;
}a[N];
int ans = 0;
int n, ct[N<<2], res[N<<2];
void push_now(int l,int r,int rt) {
if(l!=r)
ct[rt] = ct[rt << 1] + ct[rt << 1 | 1];
}
void build(int l, int r, int rt) {
ct[rt] = res[rt] = 0;
if (l == r) {
ct[rt] = 1;
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1|1);
push_now(l,r,rt);
}
void insert(int x, int k, int l, int r, int rt) {
if (l == r && ct[rt] == 1) {
ct[rt] = 0;
res[rt] = k;
return;
}
if (x <= ct[rt << 1])
insert(x, k, l, (l + r)/2, rt << 1);
else
insert(x-ct[rt<<1], k, (l + r)/2 + 1,r, rt << 1|1);
push_now(l,r,rt);
}
void putw(int l, int r, int rt) {
if (l == r) {
if (ans == 0)
cout << res[rt];
else
cout << " " << res[rt];
ans++;
return;
}
int mid = (l + r) >> 1;
putw(l, mid, rt << 1);
putw(mid + 1, r, rt << 1 | 1);
}
int main() {
while (scanf("%d", &n)!=EOF) {
ans = 0;
for (int i = 0; i < n; i++) {
scanf("%d%d", &a[i].v, &a[i].p);
a[i].v++;
}
build(1, n, 1);
for (int i = n - 1; i >= 0; i--) {
insert(a[i].v, a[i].p, 1, n, 1);
}
putw(1, n, 1);
cout << endl;
}
return 0;
}
线段树-逆序对
对于数组A,A[i]的逆序对数量为i之前比它大的数的个数。如果已经知道A中的最大值max,当我们顺序的去求A[i]的逆序对的数量的时候,其实就是找此时A中i之前A[i]到max的数有多少个。当然,对于求区间内的事情,树状数组和线段树是最为擅长的。一下代码仅仅考虑A中的元素为整数的情况,如果数太大者有double的情形需要先离散化处理。
code:
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 1100
struct node {
int w, v;
}a[N];
int b[N], p[N << 2];
int lx[N], ln[N];
bool cmp(node x, node y) {
return x.w < y.w;
}
int n;
void build(int l, int r, int rt) {
p[rt] = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
}
void insert(int l, int r, int rt, int k) {
p[rt]++;
if (l == r)return;
int mid = (l + r) >> 1;
if (k <= mid)insert(l, mid, rt << 1, k);
else insert(mid + 1, r, rt << 1 | 1, k);
}
int search(int l, int r, int rt, int k) {
if (k == r)return p[rt];
if (k <= (l + r) >> 1)
return search(l, (l + r) >> 1, rt << 1, k);
else
return p[rt << 1] + search(((l + r) >> 1) + 1, r, rt <<1 | 1, k);
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i].w);
a[i].v = i;
}
sort(a, a + n,cmp);
b[a[0].v] = 1;
int ans = 1;
for (int i = 1; i < n; i++) {
if (a[i - 1].w == a[i].w) {
b[a[i].v] = ans;
}
else {
ans++;
b[a[i].v] = ans;
}
}
build(0, n, 1);
for (int i = 0; i < n; i++) {
lx[i] = search(0, n, 1, b[i]-1);
insert(0, n, 1, b[i]);
}
build(0, n, 1);
for (int i = n - 1; i >= 0; i--) {
ln[i] = search(0, n, 1, b[i]-1);
insert(0, n, 1, b[i]);
}
long long sum = 0;
for (int i = 0; i < n; i++) {
sum += (long long)ln[i] * lx[i];
}
cout << sum << endl;
return 0;
}
poj-2777
http://poj.org/problem?id=2777
Sample Input
2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2
Sample Output
2
1
题目大意:给出T中颜色,可以给一段区域涂色,初始是全为1的颜色,然后有两种操作
(1)C x y z表示将区间x到y的颜色更改为z
(2)P x y 表示查询区间x到y的颜色种类。
题目看起来不符合区间和的条件,但是可以通过二进制转化一下。
初始化肯定都是颜色1,就表示只有一种颜色,然后每次更新颜色时,取这个数的a[x]=1<<(Item-1),a[x]中的1的位置就表示每个颜色的位置
然后pushup操作就改为:a[x]=a[x*2]|a[x*2+1],a[x]中的1的个数就是这个区间颜色的个数。
code:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e5 + 10;
int n, t, o;
int a[N << 2], b[N << 2];
void build(int l, int r, int rt) {
a[rt] = 1;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
}
void push(int rt) {
a[rt] = a[rt << 1] | a[rt << 1 | 1];
}
void pushdown(int rt) {
if (b[rt]) {
a[rt << 1] = b[rt];
a[rt << 1 | 1] = b[rt];
b[rt << 1] = b[rt];
b[rt << 1 | 1] = b[rt];
b[rt] = 0;
}
}
void change(int l, int r, int x, int y, int rt, int c) {
if (x <= l && y >= r) {
a[rt] = 1 << (c - 1);
b[rt] = 1 << (c - 1);
return;
}
int mid = (l + r) >> 1;
pushdown(rt);
if (x <= mid)
change(l, mid, x, y, rt << 1, c);
if (y > mid)
change(mid + 1, r, x, y, rt << 1 | 1, c);
push(rt);
}
int pur(int l, int r, int x, int y, int rt) {
if (x <= l && y >= r) {
return a[rt];
}
int mid = (l + r) >> 1;
pushdown(rt);
int t1 = 0, t2 = 0, ts = 0;
if (x <= mid) t1 = pur(l, mid, x, y, rt << 1);
if (y > mid) t2 = pur(mid + 1, r, x, y, rt << 1 | 1);
ts = t1 | t2;
return ts;
}
int main() {
int x, y, c;
char str[10];
scanf("%d%d%d", &n, &t, &o);
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
build(1, n, 1);
for (int i = 0; i < o; i++) {
scanf("%s", str);
if (str[0] == 'C') {
scanf("%d%d%d", &x, &y, &c);
if (x > y) {
swap(x, y);
}
change(1, n, x, y, 1, c);
}
else {
scanf("%d%d", &x, &y);
if (x > y)swap(x, y);
int ts = pur(1, n, x, y, 1);
int cnt = 0;
while (ts) {
if (ts & 1)cnt++;
ts >>= 1;
}
cout << cnt << endl;
}
}
return 0;
}
poj-2886
http://poj.org/problem?id=2886
题目大意:给出n个人的姓名和手里的一个号码,n个人排成一圈,号码有正有负,代表着正向还是反向移动A个位置,比赛从第k个人开始,把被选到的人踢出,问按踢出的顺序中因子数最多的是谁?
建立线段树,把n个人被踢的顺序找到,然后求出n个人中因子数最多的(最小的数)是谁
题目用到反素数https://blog.csdn.net/winddreams/article/details/39323039
code:
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 500005
int a[N], b[2333333];
int n, k, x, t, pos;
int ans, mx = 0;
int c[10] = { 2,3,5,7,11,13,17,19 };
void push(int rt) {
b[rt] = b[rt << 1] + b[rt << 1 | 1];
}
void build(int l, int r, int rt) {
if (l == r) {
b[rt] = 1;
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
push(rt);
}
int update(int l, int r, int rt, int h) {
b[rt]--;
if (l == r) {
pos = l;
return 1;
}
int mid = (l + r) >> 1;
if (b[rt << 1] >= h) return update(l, mid, rt << 1, h);
return update(mid+1, r, rt << 1 | 1, h - b[rt << 1])+b[rt<<1];
}
void dfs(int index, int tmp, int num) {
if (index >= 8)return;
if (num > mx) {
mx = num;
ans = tmp;
}
if (num == mx && tmp < ans)ans = tmp;
for (int i = 1; i < 25; i++) {
if (n / c[index] < tmp)break;
tmp *= c[index];
dfs(index + 1,tmp , num*(i + 1));
}
}
int main() {
string str[N];
scanf("%d%d", &n, &k);
cin.get();
for (int i = 1; i <= n; i++) {
cin >> str[i] >> a[i];
cin.get();
}
dfs(0, 1, 1);
build(1, n, 1);
for(int cnt=1;cnt<=ans;cnt++){
k = update(1, n, 1, k);
if (cnt == ans) {
cout << str[pos] << " " << mx << endl;
break;
}
if (a[pos] > 0) --k;
k = ((k + a[pos]) % (n - cnt) + (n - cnt)) % (n - cnt);
if (k == 0) k = n - cnt;
}
return 0;
}
poj-1151
http://poj.org/problem?id=1151
题目大意:给你n个矩形,求面积并,点范围大,需要离散化
从左往右或者从上往下扫描,这里是从走往右。
讲解:https://www.cnblogs.com/arbitrary/p/3202630.html
高级数据结构 林厚从里的第143页 讲的很好。
code:
#include
#include
#include
#include
#include
#include
#include
poj-1177 矩阵周长并
http://poj.org/problem?id=1177
题解:https://blog.csdn.net/qq_42037034/article/details/80341858
矩形周长并,求轮廓周长
这题可以不离散化做的,我做的是离散化以后的
code:
#include
#include
#include
#include
#include
#include
扫描线总结:https://blog.csdn.net/acmdream/article/details/73301963
poj 1037
http://poj.org/problem?id=1037
很水的一题,从开始遍历一遍输入的x坐标,求对于前n-1的x坐标小于第n个x坐标的个数。
给出n个星星的坐标,如果一个星星的左下方(包含正左和正下)有k颗星星,就说这颗星星是k级的,统计每个等级有多少个点。这题可用树状数组,对于每个星星按y坐标从小到大排序,相同y坐标按x坐标从小到大排序(题目中数据已经有序),输入顺序已排好序,那么只要依次统计星星i之前x坐标小于等于i.x的星星有多少,即是星星i的级别
code:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 15005
struct node {
int x, y;
}a[N];
int b[32002<<2];
int n;
int vis[N];
int searchs(int l, int r, int c, int k) {
if (k == r)return b[c];
int mid = (l + r) >> 1;
if (k <= mid) return searchs(l, mid, c << 1, k);
else return b[c << 1] + searchs(mid + 1, r, c << 1 | 1, k);
}
void insert(int l, int r, int c, int k) {
b[c] += 1;
if (l == r)return;
int mid = (l + r) >> 1;
if (k <= mid) insert(l, mid, c << 1, k);
else insert(mid + 1, r, c << 1 | 1, k);
}
int main() {
memset(vis, 0, sizeof(vis));
memset(b, 0, sizeof(b));
int mx = 32002;
scanf("%d", &n);
int ans;
for (int i = 0; i < n; i++) {
scanf("%d%d", &a[i].x, &a[i].y);
ans = searchs(1, mx, 1, a[i].x + 1);
vis[ans]++;
insert(1, mx, 1, a[i].x + 1);
}
for (int i = 0; i < n; i++)
cout << vis[i] << endl;
return 0;
}