MMX指令学习: Alpha Blend
3 评论157次阅读2009.04.07 13:47 作者:Felicia 编辑
Andre Lamothe在他的大作《3D游戏编程大师技巧》一书中提到,对游戏程序员来说,速度就是生命。因此使用优化技术来提高程序执行速度必不可少。其中SIMD编程占据很重要的位置。首先从MMX学起。以下转载自网上,留作学习之用。
MMX版本的Alpha Blend算法实现
这次我们的目标是: 超越普通的CPU玩家,用CPU的母语来优化程序!
MMX技术到现在来说可以算是基本大众化了,目前大多数个人电脑都应该能支持它。P55C, K6, PII, PIII…按照惯例,Intel公司将在以后的x86版本永远支持它。
(全文 …)
[计算几何] pku1444 长方体旋转
3 评论24次阅读2008.01.23 21:07 作者:Felicia 编辑
题目意思很简单,已知长方体表面上两个点,要求这两个点的最短表面距离。
一开始我是手推展开方式的,后来发现一共有12种展开情况,手写坐标变换相当麻烦。
然后改用递归方式展开。具体方式是先把第一个点转到底面(xOy平面),然后对四个方向把底面翻开,把翻到的面作为新的底面。递归做下去,一直到第二个点也翻到底面上。
下面是我的代码:
Author: WHU_GCC
Created Time: 2008-1-23 19:34:33
File Name: 1444.cpp
Description:
***********************************************************************/
#include <iostream>
using namespace std;
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7fffffff;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
int ans;
void walk(int i, int j, int x0, int y0, int x, int y, int z, int l, int w, int h)
{
if (z == 0)
ans <?= (x0 - x) * (x0 - x) + (y0 - y) * (y0 - y);
else
{
if (i >= 0 && i < 2)
walk(i + 1, j, x0, y0 - w, x, z, w - y, l, h, w);
if (i <= 0 && i > -2)
walk(i - 1, j, x0, y0 + h, x, h - z, y, l, h, w);
if (j >= 0 && j < 2)
walk(i, j + 1, x0 - l, y0, z, y, l - x, h, w, l);
if (j <= 0 && j > -2)
walk(i, j - 1, x0 + h, y0, h - z, y, x, h, w, l);
}
}
int main()
{
int l, w, h, x1, y1, z1, x2, y2, z2;
while (scanf("%d%d%d", &l, &w, &h) != EOF)
{
scanf("%d%d%d", &x1, &y1, &z1);
scanf("%d%d%d", &x2, &y2, &z2);
if (z1 != 0 && z1 != h)
{
if (y1 != 0 && y1 != w)
{
swap(x1, z1);
swap(x2, z2);
swap(l, h);
}
else
{
swap(y1, z1);
swap(y2, z2);
swap(w, h);
}
}
if (z1 == h)
{
z1 = 0;
z2 = h - z2;
}
ans = maxint;
walk(0, 0, x1, y1, x2, y2, z2, l, w, h);
printf("%d\n", ans);
}
return 0;
}
[杂题] pku3269 曼哈顿距离的性质
发表评论35次阅读2008.01.20 11:22 作者:Felicia 编辑
题目意思是给出平面上n个不相邻的点,要求到这n个点的曼哈顿距离之和最小的点的个数ans2,和这个最小距离ans1。
点的坐标范围是-10000到10000(这个很重要)
考虑到曼哈顿距离的定义:|x – x0| + |y – y0|
这个定义是线性的,对称的
因此考虑分别在两个方向上解这个问题(曼哈顿距离的性质)
- 对于x方向,定义x曼哈顿距离是|x – x0|,因为坐标范围很小,所以可以对所有可能的坐标,做一个线性扫描,求出fx,fx[x0]表示所有点到x0这个x坐标的x曼哈顿距离之和。对y方向相应地求出fy。
- 有了fx和fy,我们可以用O(1)时间算出所有点到某一坐标(x0, y0)的曼哈顿距离之和ans1。现在选出fx[x0] + fy[y0]的最小值(剔除那些输入的点),这一步我是把fx和fy分别排序做的,复杂度O(nlogn)。应该能利用点的不相邻性质做得更好。
- 现在统计满足题意的点的个数。利用fx计算数组nx,nx[i].coor表示此点的x坐标,nx[i].num表示x坐标是nx[i].coor的点的个数,nx[i].dist表示所有点到nx[i].coor的x曼哈顿距离之和。相应地计算ny。计算数组dd,dd[i]表示所有点到第i个点的曼哈顿距离之和。令t1 = sum(nx[i].num * ny[i].num), if (nx[i].dist + ny[i].dist == ans1)。令t2 = (dd[i] == ans1) 的个数。因此ans2 = t1 – t2。
下面是我的代码:
Author: WHU_GCC
Created Time: 2008-1-19 20:26:13
File Name: pku3269.cpp
Description:
**********************************************************************/
#include <iostream>
#include <set>
using namespace std;
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7FFFFFFF;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
const int maxn = 10010;
struct node_t
{
int dist;
int coor;
};
struct num_t
{
int dist;
int num;
};
bool operator <(const node_t &a, const node_t &b)
{
return a.dist < b.dist;
}
int n;
int x[maxn];
int y[maxn];
node_t fx[maxn * 2];
node_t fy[maxn * 2];
int hx[maxn * 2];
int hy[maxn * 2];
int minx, miny, maxx, maxy;
int dd[maxn];
set <pair<int, int> > ptrset;
num_t nx[maxn];
num_t ny[maxn];
int lnx, lny;
int main()
{
scanf("%d", &n);
memset(hx, 0, sizeof(hx));
memset(hy, 0, sizeof(hy));
minx = miny = maxint;
maxx = maxy = 0;
ptrset.clear();
for (int i = 0; i < n; i++)
{
scanf("%d%d", &x[i], &y[i]);
x[i] += 10000;
y[i] += 10000;
ptrset.insert(make_pair(x[i], y[i]));
hx[x[i]]++;
hy[y[i]]++;
minx <?= x[i];
miny <?= y[i];
maxx >?= x[i];
maxy >?= y[i];
}
fx[minx].dist = 0;
fx[minx].coor = minx;
for (int i = minx + 1; i <= maxx; i++)
fx[minx].dist += abs(i - minx) * hx[i];
int numrx = n - hx[minx];
int nummx = hx[minx];
int numlx = 0;
for (int i = minx + 1; i <= maxx; i++)
{
fx[i].dist = fx[i - 1].dist + numlx + nummx - numrx;
fx[i].coor = i;
numlx += nummx;
nummx = hx[i];
numrx -= nummx;
}
fy[miny].dist = 0;
fy[miny].coor = miny;
for (int i = miny + 1; i <= maxy; i++)
fy[miny].dist += abs(i - miny) * hy[i];
int numry = n - hy[miny];
int nummy = hy[miny];
int numly = 0;
for (int i = miny + 1; i <= maxy; i++)
{
fy[i].dist = fy[i - 1].dist + numly + nummy - numry;
fy[i].coor = i;
numly += nummy;
nummy = hy[i];
numry -= nummy;
}
for (int i = 0; i < n; i++)
dd[i] = fx[x[i]].dist + fy[y[i]].dist;
sort(fx + minx, fx + maxx + 1);
sort(fy + miny, fy + maxy + 1);
int ans1;
for (int i = minx; i <= maxx; i++)
for (int j = miny; j <= maxy; j++)
{
if (ptrset.count(make_pair(fx[i].coor, fy[j].coor)) == 0)
{
ans1 = fx[i].dist + fy[j].dist;
goto end;
}
}
end:;
lnx = 0;
for (int i = minx; i <= maxx; i++)
{
if (i == minx)
{
nx[lnx].dist = fx[i].dist;
nx[lnx].num = 1;
lnx++;
}
else
{
if (fx[i].dist == nx[lnx - 1].dist)
nx[lnx - 1].num++;
else
{
nx[lnx].dist = fx[i].dist;
nx[lnx].num = 1;
lnx++;
}
}
}
lny = 0;
for (int i = miny; i <= maxy; i++)
{
if (i == miny)
{
ny[lny].dist = fy[i].dist;
ny[lny].num = 1;
lny++;
}
else
{
if (fy[i].dist == ny[lny - 1].dist)
ny[lny - 1].num++;
else
{
ny[lny].dist = fy[i].dist;
ny[lny].num = 1;
lny++;
}
}
}
int ans2 = 0;
for (int i = 0; i < lnx; i++)
if (nx[i].dist <= ans1)
{
for (int j = 0; j < lny; j++)
if (nx[i].dist + ny[j].dist <= ans1)
{
if (nx[i].dist + ny[j].dist == ans1)
ans2 += nx[i].num * ny[j].num;
}
else break;
}
else break;
for (int i = 0; i < n; i++)
if (dd[i] == ans1)
ans2--;
printf("%d %d\n", ans1, ans2);
return 0;
}
[杂题] pku3263 区间性质
1个评论16次阅读2008.01.12 22:02 作者:Felicia 编辑
这个题目本质上要解决一个问题,给出一些区间[ai, bi)和一个数组,求数组中每个元素被区间覆盖的次数。
一开始想了个做法是线段树,后来想了个O(n)的做法。具体过程如下:
- 去掉重复区间
- f数组置0
- 对每个区间[ai, bi),令f[ai]++,f[bi]–
- 设答案数组为c,则c[i] = sum(f[j]), 1 <= j <= i
关键是理解f数组的意义:f[i]表示第i个点对后续点的影响,而f[ai]++,f[bi]–保证了区间外的点不受影响,区间内的点都受+1的影响
以下是我的代码:
Author: WHU_GCC
Created Time: 2008-1-12 21:14:15
File Name: pku3263.cpp
Description:
**********************************************************************/
#include <iostream>
using namespace std;
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7FFFFFFF;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
const int maxr = 10010;
const int maxn = 10010;
struct node_t
{
int l, r;
};
bool operator ==(const node_t &a, const node_t &b)
{
return a.l == b.l && a.r == b.r;
}
bool operator <(const node_t &a, const node_t &b)
{
return a.l < b.l || a.l == b.l && a.r < b.r;
}
node_t p[maxr];
int f[maxn];
int a[maxn];
int n, I, H, r;
int main()
{
scanf("%d%d%d%d", &n, &I, &H, &r);
for (int i = 0; i < r; i++)
{
scanf("%d%d", &p[i].l, &p[i].r);
if (p[i].l > p[i].r)
swap(p[i].l, p[i].r);
}
sort(p, p + r);
r = unique(p, p + r) - p;
memset(f, 0, sizeof(f));
for (int i = 0; i < r; i++)
{
f[p[i].l + 1]--;
f[p[i].r]++;
}
a[0] = 0;
for (int i = 1; i <= n; i++)
a[i] = a[i - 1] + f[i];
for (int i = 1; i <= n; i++)
printf("%d\n", a[i] + H);
return 0;
}
[杂题] SPOJ744
发表评论35次阅读2008.01.04 20:45 作者:Felicia 编辑
这个题目是Sherlock推荐我做的。
题目意思是给一个长度为n(n <= 100000)的序列,里面的数满足1 <= a[i] <= n。要找一个最长的连续子串,使得这个子串是1..k的一个排列。
昨天晚上mmd想了个n^(3/2)的算法,今天Sherlock想了个nlogn的算法。我一直在考虑有没有O(n)的算法。我觉得应该有的。
今天晚上吃饭的时候忽然想出来了。回来写了果然AC。
下面是我的算法:
注意到满足题目要求的序列必有如下性质:
- 最大值等于长度
- 必含1
- 序列和为k * (k + 1) / 2
- 序列内元素不重复
如果一个序列同时满足性质3,4,那么一定符合题目要求。
于是如果做了O(n)的预处理,可以用O(1)的时间验证某序列是否满足题目要求。
然后我的算法就顺理成章了:
- 预处理s[i],为序列的部分和
- 预处理rl[i],为从i开始往右,最长的不重复序列的末端的下标
- 预处理m[i],为从i开始,到i右边的第一个1(或者最右端),这一段数的最大值。
- 枚举左端点lp,则lp到其右边的第一个1(或者最右端)中的最大值为m[lp]。把m[lp]作为序列长度,则序列的右端点为lp + m[lp] – 1。利用s[i]和rl[i]数组可以验证这段序列是否满足题目要求,若满足,就更新最优解。
- 把输入序列反过来,重复步骤1-4。
以上每步的时间复杂度都是O(n),故算法总的时间复杂度也是O(n)。
下面是我的代码
Author: WHU_GCC
Created Time: 2008-1-4 18:06:14
File Name: spoj744.cpp
Description:
**********************************************************************/
#include <iostream>
using namespace std;
#define out(x) (cout << #x << ": " << x << endl)
typedef long long int64;
const int maxint = 0x7FFFFFFF;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template <class T> void show(T a, int n) { for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template <class T> void show(T a, int r, int l) { for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }
const int maxn = 100010;
int n;
int a[maxn];
int s[maxn];
int rl[maxn];
int hash[maxn];
int m[maxn];
int calc()
{
s[0] = 0;
for (int i = 1; i <= n; i++)
s[i] = a[i] + s[i - 1];
memset(hash, 0, sizeof(hash));
for (int i = 1, j = 1; i <= n; i++)
{
while (j <= n && hash[a[j]] == 0)
hash[a[j++]] = 1;
rl[i] = j - 1;
hash[a[i]] = 0;
}
for (int i = n; i >= 1; i--)
if (a[i] == 1)
m[i] = 1;
else if (i == n)
m[i] = a[i];
else
m[i] = max(m[i + 1], a[i]);
int ret = 0;
for (int i = 1; i <= n; i++)
if (rl[i] >= i + m[i] - 1 && s[i + m[i] - 1] - s[i - 1] == m[i] * (m[i] + 1) / 2)
ret >?= m[i];
return ret;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int ans = calc();
reverse(a + 1, a + n + 1);
ans >?= calc();
printf("%d\n", ans);
return 0;
}
