网络赛牡丹江赛区E ZOJ3813(线段树)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5345
给定序列P,定义序列S为P反复重复得到的一个无穷长的序列:
if P = 3423537, then S = 3423537342353734235373423537...
再定义G(l, r) = Sl - Sl+1 + Sl+2 - ... + (-1)r-lSr
给两种操作:
1 x d:将序列P的第x想改为d(d是一个简单数字0~9)
2 l r :求sum of G(i, j) that l <= i <= j <= r.
比赛时看到这个题,以为是求G(l, r),然后就高高兴兴用线段树开始敲了,快敲完才知道是求对任意的l <= i <= j <= r,求G(i, j) 的和
比赛快完了才想到思路,没时间敲了,今天花了大半天时间终于A掉。
按照题目要求,求sum of G(i, j) ,所以先把它化简,找下规律,很容易可以得到以下规律:
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) )
然后定义线段树节点:
- struct Node
- {
- int l, r;
- LL P[], F[][];
- }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节点的区间长度
- rep(i, , ) {
- rep(j, , ) {
- t[k].F[i][j] = ( t[rc].F[i][j] + t[lc].F[(len-i) & ][j]
- + t[lc].P[(len-i) & ] * (len-i + (len-i)%) ) % MOD;
- }
- t[k].P[i] = (t[rc].P[i] + t[lc].P[(len - i) & ]) % MOD;
- }
接下来写两个查询,分别是查询[1, p]的P值, 和查询[1,p]的F值, flag用来标注是2,4,6,8... ... 还是 1, 3, 5, 7 ... ...增长
- LL query_pre(int k, int p)
- {
- if(t[k].l == t[k].r) return t[k].P[];
- int mid = (t[k].l + t[k].r) >> ;
- if(p <= mid) return query_pre(k<<, p);
- int re = (p - mid);
- return (t[k<<].P[re&] + query_pre(k<<|, p)) % MOD;
- }
- LL query_F(int k, int p, int flag)
- {
- if(t[k].l == t[k].r) return t[k].F[][flag];
- int mid = (t[k].l + t[k].r) >> ;
- if(p <= mid) return query_F(k<<, p, flag);
- int re = p - mid;
- return (t[k<<].F[re&][flag] + t[k<<].P[re&] * (re + re%) + query_F(k<<|, p, flag)) % MOD;
- }
这样,在不考虑 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, 但是首项不同
代码还有很多需要注意的小细节,很多地方需要反复计算(比如那两个等差数列),反正就是比较麻烦
- #include <map>
- #include <set>
- #include <stack>
- #include <queue>
- #include <cmath>
- #include <ctime>
- #include <vector>
- #include <cstdio>
- #include <cctype>
- #include <cstring>
- #include <cstdlib>
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define INF 1e9
- #define inf (-((LL)1<<40))
- #define lson k<<1, L, mid
- #define rson k<<1|1, mid+1, R
- #define mem0(a) memset(a,0,sizeof(a))
- #define mem1(a) memset(a,-1,sizeof(a))
- #define mem(a, b) memset(a, b, sizeof(a))
- #define FOPENIN(IN) freopen(IN, "r", stdin)
- #define FOPENOUT(OUT) freopen(OUT, "w", stdout)
- #define rep(i, a, b) for(int i = a; i <= b; i ++)
- template<class T> T CMP_MIN(T a, T b) { return a < b; }
- template<class T> T CMP_MAX(T a, T b) { return a > b; }
- template<class T> T MAX(T a, T b) { return a > b ? a : b; }
- template<class T> T MIN(T a, T b) { return a < b ? a : b; }
- template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
- template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; }
- //typedef __int64 LL;
- typedef long long LL;
- const int MAXN = ;
- const int MAXM = ;
- const double eps = 1e-;
- const LL MOD = ;
- struct Node
- { // P[0] = S[r] + S[r-2] + S[r-4]... P[1] = S[r-1] + S[r-3] + S[r-5] + ...
- int l, r;//F[0][0] = S[r] + 3 * S[r-2] + ... F[0][1] = 2 * S[r] + 4 * S[r-2] + ...
- LL P[], F[][];// F[1][0] = S[r-1] + 3 * S[r - 3] + ... F[1][1] = 2 * S[r-1] + 4 * S[r-3] + ...
- }t[MAXN<<];
- int n, cas, q;
- char str[];
- //建树
- void build(int k, int L, int R)
- {
- t[k].l = L;
- t[k].r = R;
- t[k].P[] = t[k].P[] = ;//P和F统统初始化为0
- rep (i, , ) rep(j, , )
- t[k].F[i][j] = ;
- if(L == R) return ;
- int mid = (R + L) >> ;
- build(k<<, L, mid);
- build(k<<|, mid + , R);
- }
- //从子节点更新父节点
- void push_up(int k)
- {
- int lc = k<<, rc = k<<|;
- int re = t[rc].r - t[rc].l + ;//右侧节点长度
- rep(i, , ) {
- rep(j, , ) {
- if(re== && i==) {//特殊处理右侧只有一个节点的情况
- t[k].F[i][j] = t[lc].F[][j];
- continue;
- }
- t[k].F[i][j] = ( t[rc].F[i][j] + t[lc].F[(re-i) & ][j]
- + t[lc].P[(re-i) & ] * (re-i + (re-i)%) ) % MOD;
- }
- t[k].P[i] = (t[rc].P[i] + t[lc].P[(re - i) & ]) % MOD;
- }
- }
- //将位置为p的节点更新为val
- void update(int k, int p, int val)
- {
- if(t[k].l == t[k].r) {
- t[k].P[] = t[k].F[][] = val;
- t[k].F[][] = * val;
- return;
- }
- int m = (t[k].l + t[k].r) >> ;
- if(p <= m) update(k<<, p, val);
- else update(k<<|, p, val);
- push_up(k);
- }
- LL query_pre(int k, int p)//询问S[p] + S[p-2] + ...
- {
- if(t[k].l == t[k].r) return t[k].P[];
- int mid = (t[k].l + t[k].r) >> ;
- if(p <= mid) return query_pre(k<<, p);
- int re = (p - mid);
- return (t[k<<].P[re&] + query_pre(k<<|, p)) % MOD;
- }
- LL query_F(int k, int p, int flag)//询问F[p][flag] + F[p-2][flag] + ...
- {
- if(t[k].l == t[k].r) return t[k].F[][flag];
- int mid = (t[k].l + t[k].r) >> ;
- if(p <= mid) return query_F(k<<, p, flag);
- int re = p - mid;
- return (t[k<<].F[re&][flag] + t[k<<].P[re&] * (re + re%) + query_F(k<<|, p, flag)) % MOD;
- }
- LL get_mul(LL fir, LL d, LL k)//计算首项为fir,公差为d,项数为k的等差数列和
- {
- LL tmp1 = (k % MOD) * (fir % MOD) % MOD;
- LL tmp2 = k;
- if( k % == )
- tmp2 = k / % MOD * ((k-) % MOD) % MOD;
- else
- tmp2 = k % MOD * ((k-) / % MOD) % MOD;
- tmp2 = tmp2 * (d % MOD) % MOD;
- return (tmp1 + tmp2) % MOD;
- }
- //计算F[p][flag] + F[p-2][flag] + ... ...(这里p >> n)
- LL get_F(LL p, LL flag)
- {
- LL k = (p - ) / n;
- p = (p - ) % n + ;
- LL ans = query_F(, p, flag);
- if(n % == && k > ) {//这里n是偶数,只有一个等差数列
- ans = (ans + k % MOD * t[].F[p&][flag] ) % MOD;
- //首项为p + p%2, 公差为n, 项数为k
- ans = (ans + get_mul(p + p % , n, k) * t[].P[p&] % MOD) % MOD;
- }
- else if(n % == && k > ) {//n是奇数,会存在两个等差数列
- LL tmp = p % ? - : ;
- ans = (ans + (k+)/ % MOD * t[].F[p&][flag]) % MOD;
- ans = (ans + k/ % MOD * t[].F[!(p&)][flag]) % MOD;
- ans = (ans + get_mul(p + p%, n*, (k+)/) * t[].P[p&] % MOD) % MOD;
- ans = (ans + get_mul(p + p% + n + tmp, n*, k/) * t[].P[!(p&)] % MOD) % MOD;
- }
- return ans;
- }
- //拿到P[p] + P[p-2] + P[p-4] + ... ... (p >> n)
- LL get_P(LL p)
- {
- LL k = (p - ) / n;
- p = (p - ) % n + ;
- LL ans = query_pre(, p);
- if(n % == && k > ) {
- ans = (ans + k % MOD * t[].P[p&]) % MOD;
- }
- else if(n % == && k > ) {
- ans = (ans + (k+)/ % MOD * t[].P[p&]) % MOD;
- ans = (ans + k/ % MOD * t[].P[!(p&)]) % MOD;
- }
- return ans;
- }
- int main()
- {
- freopen("in.txt", "r", stdin);
- //freopen("out.txt", "w", stdout);
- scanf("%d%*c", &cas);
- while(cas--)
- {
- LL a, b, c;
- gets(str);
- build(, , n=strlen(str));
- for(int i = ; i < n; i ++)
- update(, i + , str[i] - '');
- scanf("%d", &q);
- while(q--) {
- scanf("%lld %lld %lld%*c", &a, &b, &c);
- if(a == ) update(, (int)b, (int)c);
- else {
- LL rf = , lf = , lp = ;
- LL len = (c - b + ), tmp = (len & ^ );
- rf = get_F( c - tmp, tmp );
- if(b > ) {
- lf = get_F( b-, tmp );
- lp = get_P( b- );
- }
- //计算节结果
- LL ans = ((rf - lf - (len + len%) % MOD * lp) % MOD + MOD) % MOD;
- printf("%lld\n", ans);
- }
- }
- }
- return ;
- }
网络赛牡丹江赛区E ZOJ3813(线段树)的更多相关文章
- hihocoder 1586 ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛-题目9 : Minimum【线段树】
https://hihocoder.com/problemset/problem/1586 线段树操作,原来题并不难..... 当时忽略了一个重要问题,就是ax*ay要最小时,x.y可以相等,那就简单 ...
- 南京网络赛G-Lpl and Energy【线段树】
During tea-drinking, princess, amongst other things, asked why has such a good-natured and cute Drag ...
- 2019南昌网络赛-I(单调栈+线段树)
题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上 ...
- ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 D 80 Days (线段树查询最小值)
题目4 : 80 Days 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 80 Days is an interesting game based on Jules Ve ...
- 2019CCPC网络赛——array(权值线段树)
题目链接http://acm.hdu.edu.cn/showproblem.php?pid=6703 题目大意: 给出一个n(n<1e5)个元素的数组A,A中所有元素都是不重复的[1,n]. 有 ...
- 2019南昌网络赛-I. Yukino With Subinterval 线段树套树状数组,CDQ分治
TMD...这题卡内存卡的真优秀... 所以以后还是别用主席树的写法...不然怎么死的都不知道... 树套树中,主席树方法开权值线段树...会造成空间的浪费...这道题内存卡的很紧... 由于树套树已 ...
- 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. ...
- 2016ACM-ICPC网络赛北京赛区 1001 (trie树牌大模拟)
[题目传送门] 1383 : The Book List 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 The history of Peking University ...
- 2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)
2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点 ...
随机推荐
- ES5下的React
按照官方推荐的思路,React使用标准的ES6标准的语法.比如说创建一个类: class Greeting extends React.Component { render() { return &l ...
- [转]通过rsync+inotify-tools+ssh实现触发式远程实时同步
文件的同步镜像在很多地方都需要用到,因此rsync这款免费软件得到了广泛的应用,包括在Windows平台上,都已经有了支持rsync的“cwRsyncServer”. 但是,我们一般都是通过结合cro ...
- IOS UI-UISearchController
ViewController.m // // ViewController.m // IOS_0224_查找联系人 // // Created by ma c on 16/2/24. // Copyr ...
- PHP---初探PHP
初探PHP 虽然说前后端分离,但作为前端还是要跟数据打交道的,所以对后台语言的了解还是很有必要的.今天要学的就是PHP. 什么是PHP? PHP(外文名:PHP: Hypertext Preproce ...
- 【51nod-1091】线段的重叠(贪心)
所有线段按起点从小到大排序,然后比较出最大的重叠部分.比如第i条线段和第j条线段进行比较找出重叠部分(j>i),当第j条线段的右端点<第i条线段的右端点,此时可以让i继续比较后面的线段:如 ...
- c# winform捕获全局异常,并记录日志
using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using ...
- c# 加密工具类
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Sec ...
- 十图详解TensorFlow数据读取机制(附代码)
在学习TensorFlow的过程中,有很多小伙伴反映读取数据这一块很难理解.确实这一块官方的教程比较简略,网上也找不到什么合适的学习材料.今天这篇文章就以图片的形式,用最简单的语言,为大家详细解释一下 ...
- Vue(1) : Vue项目入门
1.先安装nodejs环境 查看官文 2.切换到国内源 npm install -g cnpm –registry=https://registry.npm.taobao.org 执行成功后,执行如下 ...
- JMter压力测试
一. 压力测试场景设置 一般我们在做压力测试的时候,分单场景和混合场景,单场景也就是咱们压测单个接口的时候,多场景也就是有业务流程的情况下,比如说一个购物流程,那么这样的场景就是混合场景,就是有多个接 ...