数据结构

数据结构

线段树可以维护的:

  • 区间最值
  • 区间和
  • 区间最大差值
  • 区间颜色段数

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

posted @ 2019-11-12 07:26  Kuonji  阅读(138)  评论(0编辑  收藏  举报
nmdp