BZOJ 3922 Karin的弹幕 线段树

题目大意

给出一个序列,支持单点修改,每次查询一个位置成等差数列中所有数的最大值。

思路

等差数列如果公差很大的话,那么整个数列中的数并不会很多;但是如果公差很小,我们就可以用线段树来乱搞。具体方法是对于每个公差维护一个线段树,按照对这个公差取模的值来进行划分。这样询问的时候就在一块了。

具体看代码。

CODE

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 70010
#define INF 0x3f3f3f3f
using namespace std;
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)

int cnt, asks;
int src[MAX];

int tree[55][MAX << 2];
int shine[55][MAX];
int _src[55][MAX];
int _end[55][55];

void BuildTree(int tree[], int src[], int l, int r, int pos)
{
    if(l == r) {
        tree[pos] = src[l];
        return ;
    }
    int mid = (l + r) >> 1;
    BuildTree(tree, src, l, mid, LEFT);
    BuildTree(tree, src, mid + 1, r, RIGHT);
    tree[pos] = max(tree[LEFT], tree[RIGHT]);
}

void Modify(int tree[], int l, int r, int x, int c, int pos)
{
    if(l == r) {
        tree[pos] += c;
        return ;
    }
    int mid = (l + r) >> 1;
    if(x <= mid)    Modify(tree, l, mid, x, c, LEFT);
    else    Modify(tree, mid + 1, r, x, c, RIGHT);
    tree[pos] = max(tree[LEFT], tree[RIGHT]);
}

int Ask(int tree[], int l, int r, int x, int y, int pos)
{
    if(l == x && y == r)
        return tree[pos];
    int mid = (l + r) >> 1;
    if(y <= mid)    return Ask(tree, l, mid, x, y, LEFT);
    if(x > mid)     return Ask(tree, mid + 1, r, x, y, RIGHT);
    int left = Ask(tree, l, mid, x, mid, LEFT);
    int right = Ask(tree, mid + 1, r, mid + 1, y, RIGHT);
    return max(left, right);
}

int main()
{
    cin >> cnt;
    for(int i = 1; i <= cnt; ++i)
        scanf("%d", &src[i]);
    int range = min(4, cnt);
    for(int i = 1; i <= range; ++i) {
        int now = 0;
        for(int j = 1; j <= i; ++j) {
            for(int k = j; k <= cnt; k += i) {
                shine[i][k] = ++now;
                _src[i][now] = src[k];
            }
            _end[i][j] = now;
        }
        BuildTree(tree[i], _src[i], 1, cnt, 1);
    }
    cin >> asks;
    for(int flag, x, y, i = 1; i <= asks; ++i) {
        scanf("%d%d%d", &flag, &x, &y);
        if(!flag) {
            src[x] += y;
            for(int j = 1; j <= range; ++j)
                Modify(tree[j], 1, cnt, shine[j][x], y, 1);
        }
        else {
            if(y <= range)
                printf("%d\n", Ask(tree[y], 1, cnt, shine[y][x], _end[y][x % y ? x % y:y], 1));
            else {
                int ans = -INF;
                for(int j = x; j <= cnt; j += y)
                    ans = max(ans, src[j]);
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}

不知道为什么,如果把_end数组的第二维开成MAX就会RE。

哪位神犇知道告诉我一下啊。。。

时间: 2024-12-29 07:52:52

BZOJ 3922 Karin的弹幕 线段树的相关文章

【BZOJ3922】Karin的弹幕 线段树&amp;暴力

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44978599"); } 题解: 我们对每个等差数列维护一棵线段树. 比如等差为 5 , n 为 17 ,则线段树内节点顺序为: 1,6,11,16,2,7,12,17,3,8,13,4,9,14,5,10,15. 然后查询的时候到对

BZOJ 3922 Karin的弹幕

400题留念. 话说这题真是的...浪费表情. 算了一下复杂度最好的都要n√nlogn啊...这个7w闹哪样. 然而看了一眼题解,按5分. wtf我还以为有高论啊. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 70050 using namespace std; int n,m,a[maxn],tab[maxn][6],bel[m

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=

BZOJ 3211 花神游历各国 线段树题解

BZOJ 3211 花神游历各国 线段树题解 3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2551  Solved: 946[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = = 嗯就是这样,代码长度= =我写了260行......Debug了n小时= = 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #in

BZOJ 4059 Cerc2012 Non-boring sequences 线段树+扫描线

题目大意:定义一个序列为[不无聊的]当且仅当这个序列的任意一个区间都存在一个数只出现过一次,给定一个序列,要求判断这个序列是否是[不无聊的] 定义lasti表示第i个元素上一次出现的位置(第一次出现则为0),nexti表示第i个元素下一次出现的位置(最后一次出现则为n+1),那么这个元素能成为某个区间仅出现一次的数,当且仅当这个区间的左端点在[lasti+1,i]之间,右端点在[i,nexti?1]之间 我们可以将区间的左右端点放在二维平面上,那么一个元素产生的贡献是一个矩形,我们要确定的是所有

BZOJ 3196 二逼平衡树 线段树+treap

题意:链接 方法:线段树+treap的模板题 题解: 首先是对于整个树的定义,其实treap部分并没有什么区别,只不过是单root改变为多root而已. #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define lson l,mid,rt<<1 #define rson mid+1,

BZOJ 3307 雨天的尾巴 线段树

题目大意:给定一棵树,有m个操作,每次给一条路径上的每个点分发一个颜色为z的物品,所有操作结束后输出每个点上数量最多的是哪种物品 对于每个操作,我们在x和y上各打上一个插入z的标记,然后在LCA(x,y)和Fa(LCA(x,y))上各打上一个删除z的标记 然后我们对z维护线段树 DFS一遍,对于每个节点进行如下操作: 1.将所有子节点的线段树合并过来 2.处理标记,该插♂入的插♂入,该删除的删除 3.查询最大值作为答案 这样的复杂度是O(mlogm)的 然而跑不过树链剖分什么鬼--是我没离散化的

BZOJ 1858 SCOI2010 序列操作 线段树

题目大意:给定一个01序列,提供三种操作: 0:把一段区间的全部元素都变成0 1:把一段区间的全部元素都变成1 2:把一段区间内的全部元素全都取反 3:查询一段区间内1的个数 4:查询一段区间内最长的一段连续的1 首先假设没有操作4这就是bitset的水题... 多了这个,我们考虑线段树 线段树的每个节点存改动标记和翻转标记,以及该区间的信息 尽管查询的信息都是1 可是我们要连0一起保存 由于翻转后0就变成了1 1就变成了0 区间信息包含: 左端点的元素 右端点的元素 左端点開始的最长的连续元素