数据结构
数据结构
线段树可以维护的:
- 区间最值
- 区间和
- 区间最大差值
- 区间颜色段数
BZOJ1227: [SDOI2009]虔诚的墓主人
题意:
坐标上种有一些树
定义一个点的十字架数量为, 正的上下左右方向各选\(k(\le 10)\)棵树的方案数
然后种有树的坐标上数量为\(0\)
求整张图上有多少十字架
Sol:
按行枚举
用数据结构维护X坐标区间上下选的方案数
对于每一行枚举所有点, 根据上述方案数 * 左右选的方案数累加答案
讨论一下推移到下一行, 这个X坐标上有树没树的变化, 并记录这个X坐标上, 大于/小于Y的有几个, 就可以更新上下选的方案数了
LL Comb(LL n)
{
if (m > n) return 0;
return C[n][m];
}
namespace BIT
{
LL val[MAXN];
int lowbit(int x) { return x & (-x); }
void add(int x, int v)
{
for (; x <= n; x += lowbit(x)) (val[x] += v) %= MOD;
}
LL query(int x)
{
LL ret = 0;
for (; x >= 1; x -= lowbit(x)) (ret += val[x]) %= MOD;
return ret;
}
}
int upp[MAXN], low[MAXN], last[MAXN];
void solve(int Y, int l, int r)
{
for (int i = l; i <= r; ++ i)
{
int X = a[i].x;
LL now = Comb(upp[X] - 1) * Comb(low[X]) % MOD;
LL pre = Comb(upp[X]) * Comb(low[X]) % MOD;
-- upp[X];
BIT :: add(X, (now - pre + MOD) % MOD);
last[X] = Y;
}
for (int i = l + 1; i <= r; ++ i)
{
(ans += (BIT :: query(a[i].x - 1) - BIT :: query(a[i - 1].x) + MOD) % MOD * Comb(i - l) % MOD * Comb(r - i + 1) % MOD) %= MOD;
}
for (int i = l; i <= r; ++ i)
{
int X = a[i].x;
LL now = Comb(upp[X]) * Comb(low[X] + 1) % MOD;
LL pre = Comb(upp[X]) * Comb(low[X]) % MOD;
low[X] ++;
BIT :: add(X, (now - pre + MOD) % MOD);
last[X] = Y;
}
}
int main()
{
n = in(); n = in();
n = in();
for (int i = 1; i <= n; ++ i)
a[i] = (Poi) { in(), in() };
m = in();
init();
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; ++ i)
if (a[i].y > 0) ++ upp[a[i].x];
for (int Y = 1, i = 1; Y <= n && i <= n; ++ Y)
{
while (a[i].y < Y && i <= n) ++ i;
if (a[i].y != Y) continue;
int l = i, r = i;
while (a[i + 1].y == Y && i + 1 <= n) ++ i;
r = i;
solve(Y, l, r);
++ i;
}
printf("%lld\n", ans);
return 0;
}
BZOJ3531: [Sdoi2014]旅行
题意:
有\(N\)个城市的树, 每个城市有一个信仰\(C[i]\)和点权\(W[i]\), 如果一个人走一条起终点信仰相同的简单路径, 他会记录路径上和起终点信仰相同的城市的权值
有以下操作: 改某个城市的信仰/权值, 求一条路径记录的点权的最大值/和
Sol:
先树链剖分, 得到一个序列, 然后为每一种信仰动态开点建一棵位置线段树, 修改就加减一下, 查询就跳重链查
动态开点空间是\(nlogn\)的, 每次查询\(log^2n\)
BZOJ3932: [CQOI2015]任务查询系统
题意:
有\(N\)个任务, 每个任务有开始/结束时间,和权值
同一时间可能会有多个任务同时进行, 也可能有多个任务权值相同
强制在线查询某个时间点, 按权值从小到大的前\(k\)个权值的和(每次\(k\)不同)
Sol:
差分, 将任务拆成两个点, 然后按时间排序, 依次插入主席树
一个时间点可能插多次, 第一次以上一个时间为模板插入这个点, 之后的以自己为模板一样操作即可
查询不用多说了, 像二叉查找树一样搜下去即可
BZOJ1926: [Sdoi2010]粟粟的书架
给出一个矩阵, 上面有数, 每次查询求至少选多少个加起来能超过或等于某数
数据范围很微妙:
- 对于50%的数据,满足\(R, C≤200,M≤200,000\);
- 另有50%的数据,满足\(R=1,C≤500,000,M≤20,000\);
- 对于100%的数据,满足\(1≤Pi,j≤1,000,1≤Hi≤2,000,000,000\)。
Sol:
我以为两种都是主席树做的, 然后我寻思不管怎么预处理每个点都会含有\(n^2\)级别的数
意味着每个坐标暴力插\(n^2\)次, 还是继承上一行插\(n\)次都不会T, 但是无论如何都会MLE
其实\(200*200\)不用数据结构直接暴力即可...
剩下一条链就不用说了
BZOJ2243: [SDOI2011]染色
注意到一个问题:
线段树的查询函数中要不要pushup()
?
每次pushdown()
操作时, 会更新这个点的信息为正确值, 如果不pushup()
, 那么祖先节点的值仍是旧值
即pushup()
的目的是为了更新没有标记的祖先节点, 而不是有标记的点
其实不pushup()
应当是正确的:
- 修改时, 从根节点沿途到最后一个节点都有
pushdown()
和pushup()
操作 - 查询时, 如果遇到一个未
pushdown()
的节点, 说明他是修改/查询时的最后一个节点, 即祖先均被更新, 不需要pushup()
;而从这个节点继续往下推移, 就更新了沿途节点的值, 仍然不需要pushup()
- 总结: 不需要
pushup()
BZOJ2002弹飞
两棵树, 求\(\sum_i \sum_j dis1[i][j]*dis2[i][j]\)
dsu on tree http://codeforces.com/blog/entry/44351
点分治树
kdtree
bzoj4566, 2870, 3277, 3926, 3473