http://poj.org/problem?id=2991
有一个吊车由很多个不同长度的线段组成,一开始是一条长直线起点在(0,0),尾节点在(0,sum[n]),每条线段之间的夹角的初始值是180度。然后有一些操作a、 b将第a条线段和a+1之间的夹角变成b度,经过每一次操作都要求出尾节点的坐标。
线段树区间更新。
结点值保存该区间的向量及旋转角(注意他给出的不是旋转角)一个区间的向量值=左子区间的向量+右子区间的向量。
求一个向量(x0,y0)逆时针旋转B度后的向量有一个公式:
x1= x0 * cosB - y0 * sinB
y1 = x0 * sinB + y0 * cosB
顺时针就把-B代入:
x1= x0 * cosB + y0 * sinB
y1 = -x0 * sinB + y0 * cosB
这个题的数学原理还没有完全理解,有时间重新看看。
Source Code
Problem: 2991 User: liangrx06
Memory: 1220K Time: 735MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define M_PI 3.14159265358979323846
const int N = 10000;
const int C = 100000;
const int ST_SIZE = 1<<15;
int n, c;
int L[N];
int S[C], A[N];
double vx[ST_SIZE], vy[ST_SIZE];
double ang[ST_SIZE];
double prv[N];
void init(int k, int l, int r)
{
ang[k] = vx[k] = 0.0;
if (r-l == 1) {
vy[k] = L[l];
}
else {
int chl = k*2+1, chr = k*2+2;
init(chl, l, (l+r)/2);
init(chr, (l+r)/2, r);
vy[k] = vy[chl] + vy[chr];
}
}
void change(int s, double a, int v, int l, int r)
{
if (s <= l) return;
else if (s < r) {
int chl = v*2+1, chr = v*2+2;
int m = (l+r)/2;
change(s, a, chl, l, m);
change(s, a, chr, m, r);
if (s <= m) ang[v] += a;
double s = sin(ang[v]), c = cos(ang[v]);
vx[v] = vx[chl] + (c * vx[chr] - s * vy[chr]);
vy[v] = vy[chl] + (s * vx[chr] + c * vy[chr]);
}
}
int main(void)
{
while (cin >> n >> c) {
for (int i = 0; i < n; i ++)
scanf("%d", &L[i]);
init(0, 0, n);
for (int i = 1; i < n; i ++)
prv[i] = M_PI;
for (int i = 0; i < c; i ++) {
scanf("%d%d", &S[i], &A[i]);
int s = S[i];
double a = A[i] / 360.0 * 2 * M_PI;
change(s, a - prv[s], 0, 0, n);
prv[s] = a;
printf("%.2f %.2f\n", vx[0], vy[0]);
}
printf("\n");
}
return 0;
}
http://poj.org/problem?id=3264
给出一串的数字,然后给出多个区间a b,输出从a到b的最大的数和最小的数的差。
其实就是RMQ,最基本的线段树,只不过最大值和最小值都要维护。
Source Code
Problem: 3264 User: liangrx06
Memory: 1804K Time: 1860MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 50000;
const int INF = 10000000;
typedef pair<int, int> P;
int n;
P dat[4*N];
void init(int n0)
{
n = 1;
while (n < n0) n <<= 1;
for (int i = 0; i < 2*n-1; i ++) {
dat[i].first = INF;
dat[i].second = -INF;
}
}
void update(int k, int x)
{
k += n-1;
dat[k] = P(x, x);
while (k > 0) {
k = (k-1)/2;
dat[k].first = min(dat[2*k+1].first, dat[2*k+2].first);
dat[k].second = max(dat[2*k+1].second, dat[2*k+2].second);
}
}
P query(int a, int b, int k, int l, int r)
{
if (a <= l && r <= b) return dat[k];
if (a > r || b < l) return P(INF, -INF);
P vl = query(a, b, 2*k+1, l, (l+r)/2);
P vr = query(a, b, 2*k+2, (l+r)/2+1, r);
return P(min(vl.first, vr.first), max(vl.second, vr.second));
}
int main(void)
{
int n0, q;
cin >> n0 >> q;
init(n0);
int x;
for (int i = 0; i < n0; i ++) {
scanf("%d", &x);
update(i, x);
}
int a, b;
P p;
for (int i = 0; i < q; i ++) {
scanf("%d%d", &a, &b);
p = query(a-1, b-1, 0, 0, n-1);
printf("%d\n", p.second - p.first);
}
return 0;
}
http://poj.org/problem?id=2104
找出指定区间[i,j]第k小的数。
这个题平方分割和线段树都能做。
区间内第k小的数x,即<=x的有>=k个,找到最小的且满足这个条件的x即是答案。二分枚举x得到答案。
用平方分割的做法,将整个区间分成大小为b的一个个桶。每次查找<=x的个数的复杂度为:
1.对于完全包含在区间内的桶,直接二分O(logb),所以事先每个桶要先排序。
2.对于部分包含在区间内的桶,则直接线性查找O(b)。
满足1的最多有n/b个桶,满足2的最多只有两个桶,所以每次查找<=x个数的复杂度为O((n/b)*logb+b),要使复杂度取最小,b大约取sqrt(nlogn),复杂度为O(sqrt(nlogn))。再加上二分枚举答案,就是O(sqrt(nlogn)*logn)。
同时事先排序预处理复杂度为 O(nlogn)。
总体复杂度为O(nlogn+m*sqrt(nlogn)*logn)。
我按照书中代码写的,提交后TLE(估计是超了一点点,因为搜索其他人按照平方分割写的代码,好几个说是能AC也都是TLE,只有其中一个可以通过,但也濒临TLE)。因为已经掌握了思想,就懒得再改了。
只能说这个题的数据并不适合用分桶法来做。
下面贴我的TLE代码1和某能恰好通过的代码2。
书中也提到这个题可以用线段树来做,也叫归并树。见代码3.
Source Code
Problem: 2104 User: liangrx06
Memory: N/A Time: N/A
Language: C++ Result: Time Limit Exceeded
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 100000;
int B = 1000;
int n, m;
int a[N], c[N];
vector<int> b[N];
int solve(int x, int y, int k)
{
int lb = 0, ub = n;
while (ub - lb > 1) {
int mid = (ub + lb) >> 1;
int num = c[mid];
int l = x, r = y+1;
int cnt = 0;
while (l < r && l % B != 0) if(a[l++] < num) cnt ++;
while (r > l && r % B != 0) if(a[--r] < num) cnt ++;
while (l < r) {
cnt += (lower_bound(b[l/B].begin(), b[l/B].end(), num) - b[l/B].begin());
l += B;
}
if (cnt < k) lb = mid;
else ub = mid;
}
return c[lb];
}
int main(void)
{
cin >> n >> m;
B = ceil(sqrt(n * log(n*1.0)));
if(n == 1) B = 1;
for (int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
b[i/B].push_back(a[i]);
c[i] = a[i];
}
for (int i = 0; i < n/B; i ++)
sort(b[i].begin(), b[i].end());
sort(c, c+n);
int x, y, k;
for (int i = 0; i < m; i ++) {
scanf("%d%d%d", &x, &y, &k);
printf("%d\n", solve(x-1, y-1, k));
}
return 0;
}
Source Code
Problem: 2104 User: liangrx06
Memory: 3144K Time: 9954MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#define sf scanf
#define pf printf
using namespace std;
const int Maxn = 100010;
int val[Maxn],sort_val[Maxn];
int n,m,l,r,k;
vector<int> vec[Maxn];
int binary_sort(int k,int i)
{
int l = 0, r = vec[i].size() - 1;
while(l <= r)
{
int mid = (l + r) >>1;
if(vec[i][mid] <= k)
l = mid + 1;
else
r = mid - 1;
}
return r + 1;
}
int main()
{
while(~sf("%d%d",&n,&m))
{
int b = ceil(sqrt(n * log(n*1.0)));
if(n == 1) b = 1;
for(int i = 0;i < n;i ++)
{
sf("%d",&val[i]);
sort_val[i] = val[i];
vec[i/b].push_back(val[i]);
}
for(int i = 0;i <= (n-1)/b;i++)
sort(vec[i].begin(),vec[i].end());
sort(sort_val,sort_val + n);
while(m --)
{
sf("%d%d%d",&l,&r,&k);
l --;
r --;
int x = l / b;
int y = r / b;
int L = 0,R = n - 1,mid;
while(L <= R)
{
mid = (L + R) >> 1;
int key = sort_val[mid];
int ll = l,rr = r,ans = 0;
while(ll <= rr && ll < (x + 1) * b)
if(val[ll++] <= key) ans ++;
while(ll <= rr && rr >= y * b)
if(val[rr--] <= key) ans ++;
for(int i = x + 1;i < y;i ++)
ans += binary_sort(key,i);
if(ans < k)
L = mid + 1;
else if(ans >= k)
R = mid - 1;
}
pf("%d\n",sort_val[L]);
}
for(int i = 0;i <= (n-1)/ b;i++)
vec[i].clear();
}
return 0;
}
Source Code
Problem: 2104 User: liangrx06
Memory: 17264K Time: 6672MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100000;
const int ST_SIZE = 1 << 18;
int n, m;
int a[N], c[N];
vector<int> dat[ST_SIZE];
void init(int k, int l, int r)
{
if (r-l == 1) {
dat[k].push_back(a[l]);
} else {
int chl = 2*k+1, chr = 2*k+2;
int m = (l+r) / 2;
init(chl, l, m);
init(chr, m, r);
dat[k].resize(r-l);
merge(dat[chl].begin(), dat[chl].end(),
dat[chr].begin(), dat[chr].end(), dat[k].begin());
}
}
int cnt(int a, int b, int k, int l, int r, int num)
{
if (a <= l && r <= b) {
return lower_bound(dat[k].begin(), dat[k].end(), num) - dat[k].begin();
} else if (l >= b || a >= r) {
return 0;
} else {
int chl = 2*k+1, chr = 2*k+2;
int m = (l+r) / 2;
int res = cnt(a, b, chl, l, m, num);
res += cnt(a, b, chr, m, r, num);
return res;
}
}
int main(void)
{
while (scanf("%d%d", &n, &m) != EOF) {
for (int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
c[i] = a[i];
}
sort(c, c+n);
init(0, 0, n);
int x, y, k;
for (int i = 0; i < m; i ++) {
scanf("%d%d%d", &x, &y, &k);
int lb = 0, ub = n;
while (ub - lb > 1) {
int mid = (ub + lb) >> 1;
int num = c[mid];
if (cnt(x-1, y, 0, 0, n, num) < k) lb = mid;
else ub = mid;
}
printf("%d\n", c[lb]);
}
for(int i = 0; i < ST_SIZE; i++)
dat[i].clear();
}
return 0;
}
http://poj.org/problem?id=3468
给你N个数,Q个操作,操作有两种,‘Q a b ’是询问a~b这段数的和,‘C a b c’是把a~b这段数都加上c。
此题是《挑战》书中例题,线段树和树状数组都可以用,我的测试表明树状数组效率略高一些。
这里的线段树代码每个节点维护两个数据:
a. 给这个节点对应的区间内的所有元素共同加上的值
b. 在这个节点对应的区间中除去a之外其他的值的和
我看过其它的代码,方法与这里不完全一样,感觉这里的方法要更简单些。
另外树状数组的实现代码见我的博客的3.3.1节。
Source Code
Problem: 3468 User: liangrx06
Memory: 4356K Time: 1704MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100000;
typedef long long LL;
int n;
LL s1[4*N], s2[4*N];
void add(int a, int b, int x, int k, int l, int r)
{
if (a <= l && r <= b) {
s1[k] += x;
} else if (b > l && r > a) {
s2[k] += (min(b, r) - max(a, l)) * (LL)x;
int chl = 2*k+1, chr = 2*k+2;
int m = (l + r) / 2;
add(a, b, x, chl, l, m);
add(a, b, x, chr, m, r);
}
}
LL sum(int a, int b, int k, int l, int r)
{
if (a <= l && r <= b) {
return (r-l)*s1[k] + s2[k];
} else if (b > l && r > a) {
LL res = (min(b, r) - max(a, l)) * s1[k];
int chl = 2*k+1, chr = 2*k+2;
int m = (l + r) / 2;
res += sum(a, b, chl, l, m);
res += sum(a, b, chr, m, r);
return res;
} else
return 0;
}
int main(void)
{
int n, q;
cin >> n >> q;
int m;
for (int i = 0; i < n; i ++) {
scanf("%d", &m);
add(i, i+1, m, 0, 0, n);
}
char c[2];
int x, y, z;
for (int i = 0; i < q; i ++) {
scanf("%s", c);
if (c[0] == 'Q') {
scanf("%d%d", &x, &y);
printf("%lld\n", sum(x-1, y, 0, 0, n));
} else {
scanf("%d%d%d", &x, &y, &z);
add(x-1, y, z, 0, 0, n);
}
}
return 0;
}
#
http://poj.org/problem?id=
#
http://poj.org/problem?id=
#
http://poj.org/problem?id=