zoj 3813 Alternating Sum(线段树)

题目链接:zoj 3813 Alternating Sum

题目大意:给定一个P,S是以P为循环的无限串,定义G(i,j),现在有两种操作:

  • 1 x d;将P中x的位置变为d
  • 2 l r:查询S中l-r之间所有的G(i, j)的和

解题思路:线段树的区间查询点修改。

根据G(i,j)的公式可以推导出:每次查询l~r这段区间的答案为:

  • 奇数:sl?len+sl+2?(len?2)+sl+4?(len?4)+?+sr?1
  • 偶数:sl?len+sl+2?(len?2)+sl+4?(len?4)+?+sr?1?2

    于是对2倍的P建立线段树(统一奇偶操作),因为对两倍的P建树,所以修改的时候要修改两个位置。

    每个节点维护4个值:

  • sum[0];区间内与l奇偶相同的位置上数的总和
  • sum[1]:区间内与l奇偶不同的位置上数的总和
  • ans[0]:查询区间l~r时的答案
  • ans[1]:查询区间l+1~r时的答案(这里的r是相对的,根据l的奇偶性)

    区间合并时,根据左孩子的长度来决定合并操作,合并时需要考虑因为长度增加导致左孩子答案计算中的系数变化,需要乘上右孩子的区间长度,提取公因子后其实就是事先维护的sum。

    查询时,根据查询的区间大小,如果小于2P,直接插叙即可。大于2P的时候,即要将整段差分成pre,mid,suf三段处理,然后用区间合并的方式合并,注意mid是n倍的2P,在P长度比较短的情况下,n会很大,所以不能遍历计算,要推成等差数列求和,并且注意乘法爆long long。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
#define lson(x) ((x)<<1)
#define rson(x) (((x)<<1)+1)
typedef long long ll;
const int maxn = 200005;
const ll mod = 1e9+7;

struct Node {
    int l, r;
    ll sum[2], ans[2];
}nd[maxn*4];

int N, M;
char str[maxn];

void pushup(int u);
Node query(int u, int x, int y);
void modify (int u, int x, int v);
void build_segTree(int u, int l, int r);

inline ll cal(ll n) {
    if (n&1)
        return ((n-1) / 2 % mod) * (n % mod) % mod;
    else
        return (n / 2 % mod) * ((n-1) % mod) % mod;
}

void init () {
    scanf("%s", str);
    N = strlen(str);
    M = 2 * N;
    build_segTree(1, 1, M);
}

int solve (ll x, ll y) {
    ll p = (x-1) / M, q = (y-1) / M;

    if (p != q) {
        int d = (x % 2) ^ 1;
        ll n = (q - p - 1) % mod;

        Node pre = query(1, (x-1) % M + 1, M);
        Node mid = query(1, 1, M);
        Node suf = query(1, 1, (y-1) % M + 1);

        // pre;
        ll ret = (pre.ans[0] + pre.sum[0] * (n * M % mod)) % mod;
        ll s = (pre.sum[0] + mid.sum[d] * n) % mod;

        // mid;
        ret = (ret + mid.ans[d] * n) % mod;
        //ret = (ret + (mid.sum[d] * (((n&1) ? ((n-1)/2)%mod*n : (n/2)%mod*(n-1))%mod)) * M)  % mod;
        ret = (ret + (mid.sum[d] * cal(n) % mod) * M)  % mod;

        // suf;
        ret = (ret + s * (suf.r - suf.l + 1) + suf.ans[d]) % mod;
        return ret;
    } else
        return query(1, (x-1) % M + 1, (y-1) % M + 1).ans[0];
}

int main () {
    int cas, n, k;
    ll x, y;

    scanf("%d", &cas);
    while (cas--) {
        init();

        scanf("%d", &n);
        while (n--) {
            scanf("%d%lld%lld", &k, &x, &y);

            if (k == 1) {
                modify(1, x, y);
                modify(1, x + N, y);
            } else
                printf("%d\n", solve(x, y));
        }
    }
    return 0;
}

void pushup (int u) {
    int d = (nd[lson(u)].r - nd[lson(u)].l + 1) & 1;
    int L = (nd[rson(u)].r - nd[rson(u)].l + 1) % mod;

    for (int i = 0; i < 2; i++) {
        nd[u].sum[i] = (nd[lson(u)].sum[i] + nd[rson(u)].sum[i^d]) % mod;
        nd[u].ans[i] = (nd[lson(u)].ans[i] + nd[lson(u)].sum[i] * L % mod + nd[rson(u)].ans[i^d]) % mod;
    }
}

Node query(int u, int l, int r) {
    if (l == nd[u].l && nd[u].r == r)
        return nd[u];

    int mid = (nd[u].l + nd[u].r) / 2;
    if (l > mid)
        return query(rson(u), l, r);
    else if (r <= mid)
        return query(lson(u), l, r);
    else {
        Node ret;
        ret.l = l; ret.r = r;

        Node lc = query(lson(u), l, mid);
        Node rc = query(rson(u), mid + 1, r);

        int d = (lc.r - lc.l + 1) & 1;
        int L = (rc.r - rc.l + 1) % mod;

        for (int i = 0; i < 2; i++) {
            ret.sum[i] = (lc.sum[i] + rc.sum[i^d]) % mod;
            ret.ans[i] = (lc.ans[i] + lc.sum[i] * L % mod + rc.ans[i^d]) % mod;
        }

        return ret;
    }
}

void modify (int u, int x, int v) {
    if (nd[u].l == nd[u].r) {
        nd[u].sum[0] = nd[u].ans[0] = v;
        nd[u].sum[1] = nd[u].ans[1] = 0;
        return;
    }

    int mid = (nd[u].l + nd[u].r) / 2;
    if (x <= mid)
        modify(lson(u), x, v);
    if (x > mid)
        modify(rson(u), x, v);
    pushup(u);
}

void build_segTree(int u, int l, int r) {
    nd[u].l = l; nd[u].r = r;

    if (l == r) {
        int v = str[(l-1)%N] - ‘0‘;
        nd[u].sum[0] = nd[u].ans[0] = v;
        nd[u].sum[1] = nd[u].ans[1] = 0;
        return ;
    }

    int mid = (l + r) / 2;
    build_segTree(lson(u), l, mid);
    build_segTree(rson(u), mid + 1, r);
    pushup(u);
}

时间: 2024-10-10 07:06:22

zoj 3813 Alternating Sum(线段树)的相关文章

ZOJ 3813 Alternating Sum (牡丹江网络赛E题)

ZOJ 3813 Alternating Sum 题目链接 赛后补题中,这题真心恶心爆了 先推下公式,发现是隔一个位置,长度从最长每次减2,这样累加起来的和,然后就可以利用线段树维护,记录4个值,奇数和,偶数和,奇数答案和,偶数答案和,这样pushup的时候,对应要乘系数其实就是加上左边奇(偶)和乘上右边长度,线段树处理完,还有个问题就是查询可能横跨很多个区间,这样一来就要把区间进行分段,分成3段,然后和上面一样的方法合并即可,注意中间一段很长,不能一一去合并,可以推成等差数列,利用前n项和去搞

zoj 3813 Alternating Sum(2014ACMICPC Regional 牡丹江站网络赛 E)

Alternating Sum Time Limit: 2 Seconds      Memory Limit: 65536 KB There is a digit string S with infinite length. In addition, S is periodic and it can be formed by concatenating infinite repetitions of a base string P. For example, if P = 3423537, t

POJ 2777 &amp;&amp; ZOJ 1610 &amp;&amp;HDU 1698 --线段树--区间更新

直接将这3题 放一起了  今天在做线段树的东西 这3个都是区间更新的 查询方式互相不同 反正都可以放到一起吧 直接先上链接了 touch me touch me touch me 关于涉及到区间的修改 -- 区间更新的话 分为 增减 或者 修改 主要就是个 laze 标记 就是延迟更新 对于区间更新的写法 一般是有2种 其一 仔细划分到每个细小的区间    另一 粗略划分 反正 ==我的代码里会给出2种写法 看自己喜好 hdu 1 //线段树 成段更新 ---> 替换 根结点的查询 2 3 #i

ZOJ 2859 二维线段树

思路:自己写的第二发二维线段树1A,哈哈,看来对二维的push操作比较了解了:但是还没遇到在两个线段树中同时进行push操作的,其实这题我是想在x维和y维同时进行push操作的,但是想了好久不会,然后看到这题又给出10秒,然后想想在x维线段直接单点查询肯定也过了,然后在第二维就只有pushup操作,在第一维线段树没有pushup操作.要是在第一维也有pushup操作的话,那就不用单点查询那么慢了.不过也A了,想找题即在二维同时进行pushup和pushdown操作的. #include<iost

ZOJ 5332 Calculation(离线 + 线段树)

题目链接 :http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5332 比赛的时候没有做出来,赛后看了官方题解,虽然各种orz但是依旧只能orz(标程写得真是犀利),然后可耻的到网上找了下题解... 做法是线段树 + 离线搞, 网上那种对于[l, r]中的l从大到小排序很精妙(有一篇竟然说是以r为关键字,,,然后代码里面却是l...汗), 再注意到gcd()对于每一个起点来说,是单调递减的,这样可以乱搞了... 如果还不是很明白

ZOJ 3886 Nico Number (线段树)

题目地址:ZJU 3886 这个题需要想到一点,因为对一个数x不断取模的话,而且设定他小于模才会进行取余操作的话,那么最多只会进行logx次,因为每次取模都会使x最少折半.然后想到了这点就很好做了.对于区间取模更新操作可以直接暴力更新,维护一个最大值,如果这个区间的最大值小于模的话, 就不用继续向叶子更新了.然后其他的大于模的就更新到叶子节点. 然后对于NicoNumber来说,只有6,2的幂次和素数来说是符合的.所以可以预处理出来.然后就可以用线段树来维护了. 代码如下: #include <

【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少.1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20 Sample Input 9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3 Sample Output 17 25

ZOJ 3886 Nico number(线段树)

Nico Number Time Limit: 2 Seconds      Memory Limit: 262144 KB Kousaka Honoka and Minami Kotori are playing a game about a secret of Yazawa Nico. When the game starts, Kousaka Honoka will give Minami Kotori an array A of N non-negative integers. Ther

zoj 3511 Cake Robbery(线段树)

题目链接:zoj 3511 Cake Robbery 题目大意:就是有一个N边形的蛋糕,切M刀,从中挑选一块边数最多的,保证没有两条边重叠. 解题思路:有多少个顶点即为有多少条边,所以直接按照切刀切掉点的个数排序,然后用线段树维护剩下的还有哪些点. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int