给队友写的,顺便自己总结一下
本文总结了基础的acm所能用到的数据结构
省略了栈,队列,并查集等基础知识
RMQ
优点:编写复杂度低,不容易错。倍增的思想非常好
缺点:不支持修改操作,用处小
初级题目见课件
进阶题目推荐 HDU5289
树状数组
一个看起来很简单但是实际上用处十分大的数据结构,编写难度小于线段树,但是很多情况可以替代线段树,性价比十分高
更好的是,在同样的做法下,线段树往往比树状数组慢
初级入门本文不在赘述
进阶题目推荐:poj 3468 (本来是线段树的题目,但是最快的程序都是树状数组)
线段树
超级无敌的题目,当时高一学线段树走了许多弯路,因为线段树有太多种的写法,每看一个标程就会多一种写法,就会更加迷茫,所以推荐的做法是:找一个最好的标称学会写线段树,然后就不再看其他写法,能省很多时间
初级经典题目:poj 2777
进阶题目: poj 3468 (本题AC后可以初步理解标记的作用)
终极题目:poj 1177 (初学者不建议尝试,特别浪费时间)
因为线段树写法太多,本文贴出自己的写法,以供指导
#include
#include
#include
#include
#define intt long long
#define rep(i, j, k) for(int i = j; i <= k; i++)
using namespace std;
int m, a[100009];
intt n, l[2000009], r[2000009], add[2000009], sum[2000009];
void build (int x, intt L, intt R)
{
l[x] = L, r[x] = R;
if (L == R)
{
sum[x] = a[L];
add[x] = 0;
return;
}
intt mid = (L + R) >> 1;
build (2 * x, L, mid);
build (2 * x + 1,mid + 1, R);
sum[x] = sum[2 * x] + sum[2 * x + 1];
add[x] = 0;
return;
}
intt ask (int x, intt L, intt R)
{
if (L > r[x] || R < l[x])
return 0;
if (L <= l[x] && r[x] <= R)
return sum[x] + add[x] * (r[x] - l[x] + 1);
add[2 * x] += add[x];
add[2 * x + 1] += add[x];
add[x] = 0;
sum[x] = sum[2 * x] + add[2 * x] * ( r[2 * x] - l[2 * x] + 1) + sum[2 * x + 1] + add[2 * x + 1] * (r[2 * x + 1] - l[2 * x + 1] + 1);
return ask (2 * x, L, R) + ask (2 * x + 1, L, R);
}
void Add (int x, intt L, intt R, intt num)
{
if (L > r[x] || R < l[x])
return;
if (L <= l[x] && r[x] <= R)
{
add[x] += num;
return;
}
add[2 * x] += add[x];
add[2 * x + 1] += add[x];
add[x] = 0;
Add (2 * x, L, R, num);
Add (2 * x + 1, L, R, num);
sum[x] = sum[2 * x] + add[2 * x] * ( r[2 * x] - l[2 * x] + 1) + sum[2 * x + 1] + add[2 * x + 1] * (r[2 * x + 1] - l[2 * x + 1] + 1);
return;
}
int main()
{
while (scanf ("%lld%d", &n, &m) == 2 && n != 0)
{
rep (i, 1, n)
scanf ("%d", &a[i]);
build (1, 1, n);
while (m--)
{
getchar ();
char ch;
scanf ("%c", &ch);
if (ch == 'Q')
{
intt x, y;
scanf ("%lld%lld", &x, &y);
printf ("%lld\n", ask (1, x, y));
}
else
{
intt x, y, z;
scanf ("%lld%lld%lld", &x, &y, &z);
Add (1, x, y, z);
}
}
}
return 0;
}
平衡树的写法有很多,种类也很多,最有名的当然是红黑树一类的,但是实际竞赛中从来不会考这些东西,因为这么多中的平衡树的作用差不多,只有常数的差别,比如SBT非常难写,以此换取了常数上的优化,然后,很多时候只要保证复杂度,常数并不需要太过纠结,本人最喜欢的就是splay,因为splay功能非常强大,虽然常数十分大,但是大多数时候是足够使用的,所以建议初学者先掌握splay ,而treap之类的可以暂且不管,高中时在学习各种平衡树上浪费了大量时间,希望后来的人吸取我的教训
字符串数据结构
Trie
传说中的字母树,写法不难。巴蜀的课件中有详解
AC自动机
也不难理解,训练指南中已经讲的非常好了
后缀数组
本人曾经会过现在忘了。。这个确实麻烦。。留个坑吧。。
本文仅仅是学习的提纲而已,具体学习还要看个人,本人认为最好的方法就是先看巴蜀的课件和刘汝佳的算法竞赛入门经典训练指南入门,然后找对应的poj入门习题做,然后加大题量以达到十分熟悉的水平。
总体来说,初级的数据结构就这么多,个人认为线段树是最重要的,一定要深入理解,多做练习。