http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5345

给定序列P,定义序列S为P反复重复得到的一个无穷长的序列:

if P = 3423537, then S = 3423537342353734235373423537...

再定义G(lr) = Sl - Sl+1 + Sl+2 - ... + (-1)r-lSr

给两种操作:

1 x d:将序列P的第x想改为d(d是一个简单数字0~9)

2 l r  :求sum of G(ij) that l <= i <= j <= r.

比赛时看到这个题,以为是求G(l, r),然后就高高兴兴用线段树开始敲了,快敲完才知道是求对任意的l <= i <= j <= r,求G(i, j) 的和

比赛快完了才想到思路,没时间敲了,今天花了大半天时间终于A掉。

按照题目要求,求sum of G(ij) ,所以先把它化简,找下规律,很容易可以得到以下规律:

Sum{ G(i, j) } = (r - l + 1) * S[l]  + (r - l - 1) * S[l + 2] + (r - l - 3) * S[l + 4] + ... ...  + S[r]             (R-l+1)为奇数
                                                                                                                            + 2 * S[r-1]       (R-l+1)为偶数

注意上面相邻两项之间下标差都是2,由于不知道是需要用2*S[i] + 4*S[i-2]...  还是要用S[i] + 3*S[i-2]...,所以都保存起来

然后我们定义F[i][0] = S[i] + 3 * S[i-2] + 5 * S[i-4] +  ... ...

F[i][1] = 2 * S[i] + 4 * F[i-2] + 6 * F[i-4] +  ... ...

再定义P[i] = S[i] + S[i - 2] + S[i - 4] + ... ...

经过简单计算可以得出: 最后题目要求的Sum{ G(l, r) } = F[r] - F[l - 2] - k * P[l - 2]  (这里的F的第二维取决于(r-l+1)是奇还是偶,通过计算可以得到 令 len = (r - l + 1)  ,  那么 k = len + len % 2)  )

然后定义线段树节点:

  1. struct Node
  2. {
  3. int l, r;
  4. LL P[], F[][];
  5. }t[MAXN<<];

其中:
P[0] = S[r] + S[r - 2] + S[r - 4] + ... ... + S[k]   (k >= l)
P[0] = S[r-1] + S[r - 3] + S[r - 5] + ... ... + S[k]   (k >= l)

F[0][0] = S[r] + 3 * S[r - 2] + 5 * S[r - 4] + ... ... + x * S[k]  (k >= l)
F[0][1] = 2*S[r] + 4*S[r - 2] + 6*S[r - 4] + ... ... + x * S[k]  (k >= l)
F[1][0] = S[r-1] + 3 * S[r - 3] + 5 * S[r - 5] + ... ... + x * S[k]  (k >= l)
F[1][1] = 2*S[r-1] + 4*S[r - 3] + 6*S[r - 5] + ... ... + x * S[k]  (k >= l)

然后做线段树的单电修改,修改某个叶子节点后往上更新,  rc和lc对应于当前节点k的右子节点和左子节点,  len是rc节点的区间长度

  1. rep(i, , ) {
  2. rep(j, , ) {
  3. t[k].F[i][j] = ( t[rc].F[i][j] + t[lc].F[(len-i) & ][j]
  4. + t[lc].P[(len-i) & ] * (len-i + (len-i)%) ) % MOD;
  5. }
  6. t[k].P[i] = (t[rc].P[i] + t[lc].P[(len - i) & ]) % MOD;
  7. }

接下来写两个查询,分别是查询[1, p]的P值, 和查询[1,p]的F值, flag用来标注是2,4,6,8... ... 还是 1, 3, 5, 7 ... ...增长

  1. LL query_pre(int k, int p)
  2. {
  3. if(t[k].l == t[k].r) return t[k].P[];
  4.  
  5. int mid = (t[k].l + t[k].r) >> ;
  6.  
  7. if(p <= mid) return query_pre(k<<, p);
  8.  
  9. int re = (p - mid);
  10. return (t[k<<].P[re&] + query_pre(k<<|, p)) % MOD;
  11. }
  12.  
  13. LL query_F(int k, int p, int flag)
  14. {
  15. if(t[k].l == t[k].r) return t[k].F[][flag];
  16.  
  17. int mid = (t[k].l + t[k].r) >> ;
  18.  
  19. if(p <= mid) return query_F(k<<, p, flag);
  20.  
  21. int re = p - mid;
  22.  
  23. return (t[k<<].F[re&][flag] + t[k<<].P[re&] * (re + re%) + query_F(k<<|, p, flag)) % MOD;
  24. }

这样,在不考虑 l 和 r 大于给定序列长度n的情况下,原题已经可解,答案就是:

query_F(1, r) - query_F(1, l - 2) - query_Pre(1, l - 2) * (len + len % 2)        (其中len = r - l + 1)

然后再考虑l 和 r比较大的情况,由于虽然l 和 r 比较大,但是给定序列的长度是小于1e5的, 所以说显然中间过程是可以直接推算出来的, 事实上,设原序列长度为n, len = (r - l + 1), 那么最初给出的序列会出现 k = (len - 1) / n 次,而这 k 次序列可以看做是 出现了k次F[n],以及 出现了 a * P[n], (a + n) * P[n], (a + 2 n) * P[n]... ...(a + (k-1)*n) * P[n]

这正好是一个等差数列, 所以计算新的F[r] (r >> n)时, F[n] = query(r) + k * F[n] + P[n] * (a + (a + d) + (a + 2 * d) + ... ... + (a + (k-1) * d) ).

注意, 上面的等差数列首项a还未确定, 公差d也还未确定, 也就是说,只要计算出a 和 d, 就计算出了我们的要求的答案。

注意到如果n(原序列长度)是偶数,又因为增量是2,所以可以计算出公差 d = n,而首项 a = p + p % 2  , 其中p = (r - 1) % n + 1,也就是最后一个剩下序列的长度

如果n是奇数,会出现有时我们需要的是F[n], 有时需要的又是F[n-1],这是只要理解为两个等差数列就可以了,其中公差都是d = 2 * n, 但是首项不同

代码还有很多需要注意的小细节,很多地方需要反复计算(比如那两个等差数列),反正就是比较麻烦

  1. #include <map>
  2. #include <set>
  3. #include <stack>
  4. #include <queue>
  5. #include <cmath>
  6. #include <ctime>
  7. #include <vector>
  8. #include <cstdio>
  9. #include <cctype>
  10. #include <cstring>
  11. #include <cstdlib>
  12. #include <iostream>
  13. #include <algorithm>
  14. using namespace std;
  15. #define INF 1e9
  16. #define inf (-((LL)1<<40))
  17. #define lson k<<1, L, mid
  18. #define rson k<<1|1, mid+1, R
  19. #define mem0(a) memset(a,0,sizeof(a))
  20. #define mem1(a) memset(a,-1,sizeof(a))
  21. #define mem(a, b) memset(a, b, sizeof(a))
  22. #define FOPENIN(IN) freopen(IN, "r", stdin)
  23. #define FOPENOUT(OUT) freopen(OUT, "w", stdout)
  24. #define rep(i, a, b) for(int i = a; i <= b; i ++)
  25. template<class T> T CMP_MIN(T a, T b) { return a < b; }
  26. template<class T> T CMP_MAX(T a, T b) { return a > b; }
  27. template<class T> T MAX(T a, T b) { return a > b ? a : b; }
  28. template<class T> T MIN(T a, T b) { return a < b ? a : b; }
  29. template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
  30. template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; }
  31.  
  32. //typedef __int64 LL;
  33. typedef long long LL;
  34. const int MAXN = ;
  35. const int MAXM = ;
  36. const double eps = 1e-;
  37. const LL MOD = ;
  38.  
  39. struct Node
  40. { // P[0] = S[r] + S[r-2] + S[r-4]... P[1] = S[r-1] + S[r-3] + S[r-5] + ...
  41. int l, r;//F[0][0] = S[r] + 3 * S[r-2] + ... F[0][1] = 2 * S[r] + 4 * S[r-2] + ...
  42. LL P[], F[][];// F[1][0] = S[r-1] + 3 * S[r - 3] + ... F[1][1] = 2 * S[r-1] + 4 * S[r-3] + ...
  43. }t[MAXN<<];
  44. int n, cas, q;
  45. char str[];
  46.  
  47. //建树
  48. void build(int k, int L, int R)
  49. {
  50. t[k].l = L;
  51. t[k].r = R;
  52. t[k].P[] = t[k].P[] = ;//P和F统统初始化为0
  53. rep (i, , ) rep(j, , )
  54. t[k].F[i][j] = ;
  55. if(L == R) return ;
  56.  
  57. int mid = (R + L) >> ;
  58. build(k<<, L, mid);
  59. build(k<<|, mid + , R);
  60. }
  61.  
  62. //从子节点更新父节点
  63. void push_up(int k)
  64. {
  65. int lc = k<<, rc = k<<|;
  66. int re = t[rc].r - t[rc].l + ;//右侧节点长度
  67. rep(i, , ) {
  68. rep(j, , ) {
  69. if(re== && i==) {//特殊处理右侧只有一个节点的情况
  70. t[k].F[i][j] = t[lc].F[][j];
  71. continue;
  72. }
  73. t[k].F[i][j] = ( t[rc].F[i][j] + t[lc].F[(re-i) & ][j]
  74. + t[lc].P[(re-i) & ] * (re-i + (re-i)%) ) % MOD;
  75. }
  76. t[k].P[i] = (t[rc].P[i] + t[lc].P[(re - i) & ]) % MOD;
  77. }
  78. }
  79.  
  80. //将位置为p的节点更新为val
  81. void update(int k, int p, int val)
  82. {
  83. if(t[k].l == t[k].r) {
  84. t[k].P[] = t[k].F[][] = val;
  85. t[k].F[][] = * val;
  86. return;
  87. }
  88. int m = (t[k].l + t[k].r) >> ;
  89.  
  90. if(p <= m) update(k<<, p, val);
  91.  
  92. else update(k<<|, p, val);
  93.  
  94. push_up(k);
  95. }
  96.  
  97. LL query_pre(int k, int p)//询问S[p] + S[p-2] + ...
  98. {
  99. if(t[k].l == t[k].r) return t[k].P[];
  100.  
  101. int mid = (t[k].l + t[k].r) >> ;
  102.  
  103. if(p <= mid) return query_pre(k<<, p);
  104.  
  105. int re = (p - mid);
  106. return (t[k<<].P[re&] + query_pre(k<<|, p)) % MOD;
  107. }
  108.  
  109. LL query_F(int k, int p, int flag)//询问F[p][flag] + F[p-2][flag] + ...
  110. {
  111. if(t[k].l == t[k].r) return t[k].F[][flag];
  112.  
  113. int mid = (t[k].l + t[k].r) >> ;
  114.  
  115. if(p <= mid) return query_F(k<<, p, flag);
  116.  
  117. int re = p - mid;
  118.  
  119. return (t[k<<].F[re&][flag] + t[k<<].P[re&] * (re + re%) + query_F(k<<|, p, flag)) % MOD;
  120. }
  121.  
  122. LL get_mul(LL fir, LL d, LL k)//计算首项为fir,公差为d,项数为k的等差数列和
  123. {
  124. LL tmp1 = (k % MOD) * (fir % MOD) % MOD;
  125. LL tmp2 = k;
  126. if( k % == )
  127. tmp2 = k / % MOD * ((k-) % MOD) % MOD;
  128. else
  129. tmp2 = k % MOD * ((k-) / % MOD) % MOD;
  130. tmp2 = tmp2 * (d % MOD) % MOD;
  131. return (tmp1 + tmp2) % MOD;
  132. }
  133.  
  134. //计算F[p][flag] + F[p-2][flag] + ... ...(这里p >> n)
  135. LL get_F(LL p, LL flag)
  136. {
  137. LL k = (p - ) / n;
  138. p = (p - ) % n + ;
  139. LL ans = query_F(, p, flag);
  140. if(n % == && k > ) {//这里n是偶数,只有一个等差数列
  141. ans = (ans + k % MOD * t[].F[p&][flag] ) % MOD;
  142. //首项为p + p%2, 公差为n, 项数为k
  143. ans = (ans + get_mul(p + p % , n, k) * t[].P[p&] % MOD) % MOD;
  144. }
  145. else if(n % == && k > ) {//n是奇数,会存在两个等差数列
  146. LL tmp = p % ? - : ;
  147.  
  148. ans = (ans + (k+)/ % MOD * t[].F[p&][flag]) % MOD;
  149.  
  150. ans = (ans + k/ % MOD * t[].F[!(p&)][flag]) % MOD;
  151.  
  152. ans = (ans + get_mul(p + p%, n*, (k+)/) * t[].P[p&] % MOD) % MOD;
  153.  
  154. ans = (ans + get_mul(p + p% + n + tmp, n*, k/) * t[].P[!(p&)] % MOD) % MOD;
  155. }
  156. return ans;
  157. }
  158.  
  159. //拿到P[p] + P[p-2] + P[p-4] + ... ... (p >> n)
  160. LL get_P(LL p)
  161. {
  162. LL k = (p - ) / n;
  163. p = (p - ) % n + ;
  164. LL ans = query_pre(, p);
  165. if(n % == && k > ) {
  166. ans = (ans + k % MOD * t[].P[p&]) % MOD;
  167. }
  168. else if(n % == && k > ) {
  169. ans = (ans + (k+)/ % MOD * t[].P[p&]) % MOD;
  170.  
  171. ans = (ans + k/ % MOD * t[].P[!(p&)]) % MOD;
  172. }
  173. return ans;
  174. }
  175.  
  176. int main()
  177. {
  178. freopen("in.txt", "r", stdin);
  179. //freopen("out.txt", "w", stdout);
  180. scanf("%d%*c", &cas);
  181. while(cas--)
  182. {
  183. LL a, b, c;
  184. gets(str);
  185. build(, , n=strlen(str));
  186. for(int i = ; i < n; i ++)
  187. update(, i + , str[i] - '');
  188. scanf("%d", &q);
  189. while(q--) {
  190. scanf("%lld %lld %lld%*c", &a, &b, &c);
  191. if(a == ) update(, (int)b, (int)c);
  192. else {
  193. LL rf = , lf = , lp = ;
  194. LL len = (c - b + ), tmp = (len & ^ );
  195. rf = get_F( c - tmp, tmp );
  196. if(b > ) {
  197. lf = get_F( b-, tmp );
  198. lp = get_P( b- );
  199. }
  200. //计算节结果
  201. LL ans = ((rf - lf - (len + len%) % MOD * lp) % MOD + MOD) % MOD;
  202. printf("%lld\n", ans);
  203. }
  204. }
  205. }
  206. return ;
  207. }

网络赛牡丹江赛区E ZOJ3813(线段树)的更多相关文章

  1. hihocoder 1586 ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛-题目9 : Minimum【线段树】

    https://hihocoder.com/problemset/problem/1586 线段树操作,原来题并不难..... 当时忽略了一个重要问题,就是ax*ay要最小时,x.y可以相等,那就简单 ...

  2. 南京网络赛G-Lpl and Energy【线段树】

    During tea-drinking, princess, amongst other things, asked why has such a good-natured and cute Drag ...

  3. 2019南昌网络赛-I(单调栈+线段树)

    题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上 ...

  4. ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 D 80 Days (线段树查询最小值)

    题目4 : 80 Days 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 80 Days is an interesting game based on Jules Ve ...

  5. 2019CCPC网络赛——array(权值线段树)

    题目链接http://acm.hdu.edu.cn/showproblem.php?pid=6703 题目大意: 给出一个n(n<1e5)个元素的数组A,A中所有元素都是不重复的[1,n]. 有 ...

  6. 2019南昌网络赛-I. Yukino With Subinterval 线段树套树状数组,CDQ分治

    TMD...这题卡内存卡的真优秀... 所以以后还是别用主席树的写法...不然怎么死的都不知道... 树套树中,主席树方法开权值线段树...会造成空间的浪费...这道题内存卡的很紧... 由于树套树已 ...

  7. ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛 i题 Minimum(线段树)

    描述 You are given a list of integers a0, a1, …, a2^k-1. You need to support two types of queries: 1. ...

  8. 2016ACM-ICPC网络赛北京赛区 1001 (trie树牌大模拟)

    [题目传送门] 1383 : The Book List 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 The history of Peking University ...

  9. 2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)

    2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点 ...

随机推荐

  1. ES5下的React

    按照官方推荐的思路,React使用标准的ES6标准的语法.比如说创建一个类: class Greeting extends React.Component { render() { return &l ...

  2. [转]通过rsync+inotify-tools+ssh实现触发式远程实时同步

    文件的同步镜像在很多地方都需要用到,因此rsync这款免费软件得到了广泛的应用,包括在Windows平台上,都已经有了支持rsync的“cwRsyncServer”. 但是,我们一般都是通过结合cro ...

  3. IOS UI-UISearchController

    ViewController.m // // ViewController.m // IOS_0224_查找联系人 // // Created by ma c on 16/2/24. // Copyr ...

  4. PHP---初探PHP

    初探PHP 虽然说前后端分离,但作为前端还是要跟数据打交道的,所以对后台语言的了解还是很有必要的.今天要学的就是PHP. 什么是PHP? PHP(外文名:PHP: Hypertext Preproce ...

  5. 【51nod-1091】线段的重叠(贪心)

    所有线段按起点从小到大排序,然后比较出最大的重叠部分.比如第i条线段和第j条线段进行比较找出重叠部分(j>i),当第j条线段的右端点<第i条线段的右端点,此时可以让i继续比较后面的线段:如 ...

  6. c# winform捕获全局异常,并记录日志

    using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using ...

  7. c# 加密工具类

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Sec ...

  8. 十图详解TensorFlow数据读取机制(附代码)

    在学习TensorFlow的过程中,有很多小伙伴反映读取数据这一块很难理解.确实这一块官方的教程比较简略,网上也找不到什么合适的学习材料.今天这篇文章就以图片的形式,用最简单的语言,为大家详细解释一下 ...

  9. Vue(1) : Vue项目入门

    1.先安装nodejs环境 查看官文 2.切换到国内源 npm install -g cnpm –registry=https://registry.npm.taobao.org 执行成功后,执行如下 ...

  10. JMter压力测试

    一. 压力测试场景设置 一般我们在做压力测试的时候,分单场景和混合场景,单场景也就是咱们压测单个接口的时候,多场景也就是有业务流程的情况下,比如说一个购物流程,那么这样的场景就是混合场景,就是有多个接 ...