交错和查询

Time Limit: 10 Sec  Memory Limit: 256 MB

Description

  无限循环数字串S由长度为n的循环节s构成。设s为12345(n=5),则数字串S为123451234512345…
  设Si为S的第i位数字,在上面的例子中,S1=1,S2=2,S6=1。
  设S的一个子串S[l,r]的交错和为sum(l,r):
  sum(l,r) = Sl - S1+1 + Sl+2- Sl+3 + … + (-1)r-lSr
  如sum(2,7) = 2 - 3 + 4 - 5 + 1 - 2 = -3
  现给定循环节s,要求支持两种操作:
  1 pos digit:修改循环节s上的某一位,即将spos改为digit。
  2 l r:求S[l,r]内所有子串的交错和的和,即输出ans对10^9+7的模。

Input

  第一行一个整数n,表示循环节s的长度。
  第二行一个长度为n的数字串,表示循环节s。
  第三行一个整数m,表示操作次数。
  以下m行,每行3个整数。
  若第一个数为1,表示是修改操作1 pos digit。
  若第一个数为2,表示是询问操作2 l r。

Output

  对于每个询问操作输出一行,表示答案。

Sample Input 

  5
  12345
  5
  2 1 5
  2 6 10
  1 3 5
  2 1 5
  2 1 6

Sample Output

  19
  19
  25
  36

HINT

  对于10%的数据点,n, m <= 50;
  对于20%的数据点,n, m <=1000;
  对于40%的数据点,1 <= l<= r <= n;
  对于100%的数据点,n, m <=200000;1 <= l <= r <= 1018;1 <= pos <= n;0 <= digit <= 9;

Main idea

  给定两种操作:1.修改循环节上的某一位;2.询问[l,r]的所有子串和。

Solution

  首先轻易地找到了规律,发现对于区间[l,r],只有奇数位置上的值会对答案产生影响,并且:,然后我们拆开式子得到:。现在考虑如何用一个数据结构来维护这个Ans,这里采用线段树。

  我们分几步来实现:

  第一步:
  我们先考虑l,r在一个循环节内的情况。显然对于线段树上的每个节点维护五个信息:len, odd.val, odd.val_i, eve.val, eve.val_i分别表示区间长度、奇数位置的和、奇数位置*i的和、偶数位置的和、偶数位置*i的和,那么我们上传合并线段树的时候判断一下区间长度的奇偶即可。举个例子:比如现在左区间长度为3,要更新奇数位置的值,就是左区间的奇数位置和 加上 右区间的偶数位置和,我们重载运算符判断一下即可。这样操作我们就可以得到Σai以及Σai*i。

  第二步:
  (1)  我们再来考虑一下l,r不在一个循环节内的情况。显然我们可以将区间拆成三部分:左段、中段、右段,其中中段是包含所有的1-n的整体,而左段和右段则是~n或者1~的一部分

  (2)  然后我们显然可以很轻易地通过计算一下x,y的间隔块数以及若干信息来算出Σai。

  (3)  那么式子后面的Σai*i怎么办呢?我们发现:我们将序列分为若干段,显然每一段增加的值是一样的,那么我们就可以将Σai*i(这里的i是实际位置)拆成:Σai*i (在一个循环节中的位置) + Σai*(所在块数-1)*n。

  (4)  然后我们中段块数一定不为1,要怎么办呢?举个例子,比如循环节长度为10,我们要求2~4段的Σ,那么显然就是Σai*n*(i+1+2),惊讶地发现中间的一个等差数列,那么我们要乘的就是一个等差数列的和了。

  (5)  然后三段中到底是统计奇数位置的和还是统计偶数位置的和呢?发现较难处理,于是我们可以将原序列*2(拓展一倍),发现如果x是奇数,那么就加上左段的奇数位置,中段右段的奇数位置,否则加上左段的奇数位置,以及中段右段的偶数位置。

  这样我们就解决了问题,具体问题还是参见代码啦。

Code

 #include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<map>
using namespace std;
typedef long long s64; const int ONE=;
const s64 Niyu=5e8+;
const int MOD=1e9+; int n,T;
int a[ONE*];
char ch[ONE];
int Q;
s64 x,y;
s64 x_orig,y_orig,x_bloc,y_bloc,diff;
s64 A,A_i; struct power
{
int len; struct point
{
s64 val,val_i;
friend point operator +(point a,point b)
{
a.val=(a.val + b.val)%MOD;
a.val_i=(a.val_i + b.val_i) % MOD;
return a;
}
}odd,eve; friend power operator +(power a,power b)
{
power c;
c.len = a.len+b.len;
if(a.len%)
{
c.odd = a.odd+b.eve;
c.eve = a.eve+b.odd;
}
else
{
c.odd = a.odd+b.odd;
c.eve = a.eve+b.eve;
}
return c;
}
}Node[ONE*],ZERO; s64 get()
{
s64 res=,Q=;char c;
while( (c=getchar())< || c> )
if(c=='-')Q=-;
if(Q) res=c-;
while( (c=getchar())>= && c<= )
res=res*+c-;
return res*Q;
} void Build(int i,int l,int r)
{
if(l==r)
{
Node[i].len = ;
Node[i].odd.val = a[l];
Node[i].odd.val_i = a[l]*l % MOD;
return;
} int mid=(l+r)/;
Build(i*,l,mid); Build(i*+,mid+,r); Node[i] = Node[i*] + Node[i*+];
} void Update(int i,int l,int r,int L,int x)
{
if(L==l && l==r)
{
Node[i].odd.val = x;
Node[i].odd.val_i = x*l % MOD;
Node[i].eve.val = Node[i].eve.val_i = ;
return;
} int mid=(l+r)/;
if(L<=mid) Update(i*,l,mid,L,x);
else Update(i*+,mid+,r,L,x); Node[i] = Node[i*] + Node[i*+];
} power Query(int i,int l,int r,int L,int R)
{
if(L>R) return ZERO;
if(L<=l && r<=R) return Node[i]; int mid=(l+r)/;
if(R<=mid) return Query(i*,l,mid,L,R);
if(mid+<=L) return Query(i*+,mid+,r,L,R);
return Query(i*,l,mid,L,R) + Query(i*+,mid+,r,L,R);
} s64 HE(s64 a,s64 b)
{
a--; b--;
if(a==b) return ;
if(a>b) return ;
s64 x=(b-a+) % MOD;
return (s64)(a+b)%MOD*x%MOD * Niyu % MOD;
} int main()
{
ZERO.len=; ZERO.odd.val=ZERO.odd.val_i=ZERO.eve.val=ZERO.eve.val_i=; n=get();
scanf("%s",ch+);
for(int i=;i<=n;i++) a[i]=ch[i]-'';
for(int i=;i<=n;i++) a[i+n]=a[i];
n*=; Build(,,n); T=get();
while(T--)
{
Q=get();
if(Q==)
{
x=get(); y=get();
Update(,,n,x,y);
Update(,,n,x+n/,y);
}
else
{
x=get(); y=get();
x_orig=(x-)%n+; y_orig=(y-)%n+;
x_bloc=(x-)/n+; y_bloc=(y-)/n+; diff = y_bloc-x_bloc-; diff%=MOD;
if(diff!=-)
{
if(x%)
{
power res_l = Query(,,n, x_orig,n);
power res_r = Query(,,n, ,y_orig);
power res_mid = Query(,,n, ,n); A =( res_l.odd.val + res_r.odd.val + res_mid.odd.val * diff % MOD ) % MOD;
A_i=; A_i += (res_l.odd.val_i + (s64)res_l.odd.val * (x_bloc-)%MOD * n % MOD)%MOD; A_i%=MOD;
A_i += (res_r.odd.val_i + (s64)res_r.odd.val * (y_bloc-)%MOD * n % MOD)%MOD; A_i%=MOD; A_i += (diff*res_mid.odd.val_i%MOD + (s64)res_mid.odd.val * n % MOD * HE(x_bloc+,y_bloc-)%MOD)%MOD;
A_i%=MOD;
}
else
{
power res_l = Query(,,n, x_orig,n);
power res_r = Query(,,n, ,y_orig);
power res_mid = Query(,,n, ,n); A =( res_l.odd.val + res_r.odd.val + res_mid.odd.val * diff % MOD ) % MOD;
A_i=; A_i += (res_l.odd.val_i + (s64)res_l.odd.val * (x_bloc-)%MOD * n % MOD)%MOD; A_i%=MOD;
A_i += (res_r.odd.val_i + (s64)res_r.odd.val * (y_bloc-)%MOD * n % MOD)%MOD; A_i%=MOD; A_i += (diff*res_mid.odd.val_i%MOD + (s64)res_mid.odd.val * n % MOD * HE(x_bloc+,y_bloc-)%MOD)%MOD;
A_i%=MOD;
}
}
else
{
power res = Query(,,n, x_orig,y_orig);
A = res.odd.val;
A_i=;
A_i += (s64)(res.odd.val_i + A * (x_bloc-)%MOD * n % MOD) % MOD; A_i%=MOD;
}
printf("%lld\n", (s64)(A * (y%MOD+) % MOD - A_i + MOD) % MOD);
}
} }

【FJWC2017】交错和查询 [线段树]的更多相关文章

  1. bzoj3110 [Zjoi2013]K大数查询——线段树套线段树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3110 外层权值线段树套内层区间线段树: 之所以外层权值内层区间,是因为区间线段树需要标记下传 ...

  2. bzoj 3110 [Zjoi2013]K大数查询——线段树套线段树(标记永久化)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3110 第一道线段树套线段树! 第一道标记永久化! 为什么为什么写了两个半小时啊…… 本想线段 ...

  3. [HDU-5172] 单点查询线段树

    题意: 给你一个长度为n的数组v[],有m次询问,问你在区间[L,R]中是否包含区间[1,R-L+1]的全部数字,如果是输出YES,否则输出NO 题解: 区间[1,R-L+1]与区间[L,R]的长度一 ...

  4. 线段树 区间开方区间求和 & 区间赋值、加、查询

    本文同步发表于 https://www.zybuluo.com/Gary-Ying/note/1288518 线段树的小应用 -- 维护区间开方区间求和 题目传送门 约定: sum(i,j) 表示区间 ...

  5. 序列内第k小查询(线段树)

    最近请教了一下大佬怎么求序列内第k大查询,自己又捣鼓了一下,虽然还没有懂得区间第k大查询,不过姑且做一个记录先吧 因为每个元素大小可能很大而元素之间不连续,所以我们先离散化处理一下,程序中的ori[ ...

  6. hdu4831 Scenic Popularity(线段树)

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4831 题目大概意思就是有多个风景区和休息区,每个风景区有热度,休息区的热度与最接近的分景区的热度相同, ...

  7. HDU 3966 Aragorn's Story 树链剖分+树状数组 或 树链剖分+线段树

    HDU 3966 Aragorn's Story 先把树剖成链,然后用树状数组维护: 讲真,研究了好久,还是没明白 树状数组这样实现"区间更新+单点查询"的原理... 神奇... ...

  8. POJ——3264线段树

    题目: 输入两个数(m,n),m表示牛的头数,n表示查询的个数.查询时输入两个数(x,y),表示查询范围的起始值和终止值,查询结果是,这个区间内牛重量的最大值减去牛重量的最小值,数量级为1000,00 ...

  9. POJ1151-扫面线+线段树+离散化//入门题

    比较水的入门题 记录矩形竖边的x坐标,离散化排序.以被标记的边建树. 扫描线段树,查询线段树内被标记的边.遇到矩形的右边就删除此边 每一段的面积是查询结果乘边的横坐标之差,求和就是答案 #includ ...

随机推荐

  1. C#读写txt文件的两种方法介绍 v

    C#读写txt文件的两种方法介绍 1.添加命名空间 System.IO; System.Text; 2.文件的读取 (1).使用FileStream类进行文件的读取,并将它转换成char数组,然后输出 ...

  2. 使用apache的ab压力测试时失败请求原因

    只要出现 Failed requests 就会多出现一行要求失败的各原因的数据统计,分别有 Connect, Length,与 Exception 三种,分别代表的意义为:Connect      无 ...

  3. 深入浅出 Webpack

    深入浅出 Webpack 评价 Webpack 凭借强大的功能与良好的使用体验,已经成为目前最流行,社区最活跃的打包工具,是现代 Web 开发必须掌握的技能之一.作者结合自身的实战经验,介绍了 Web ...

  4. Spark集群管理器介绍

    Spark可以运行在各种集群管理器上,并通过集群管理器访问集群中的其他机器.Spark主要有三种集群管理器,如果只是想让spark运行起来,可以采用spark自带的独立集群管理器,采用独立部署的模式: ...

  5. Visual Studio 2015安装包

    点击下载

  6. Kindle 3(非常旧的版本) 隔一段时间自动重启问题

    买了本新书后,kindle 3 自己没事就在那边重启,几分钟一次 查到解决方案1: https://answers.yahoo.com/question/index?qid=2014040815565 ...

  7. 感知机学习算法(PLA)

    Perception Learning Algorithm, PLA 1.感知机 感知机是一种线性分类模型,属于判别模型. 感知机模型给出了由输入空间到输出空间的映射: f(X) = sign(WTX ...

  8. day-10 sklearn库实现SVM支持向量算法

    学习了SVM分类器的简单原理,并调用sklearn库,对40个线性可分点进行训练,并绘制出图形画界面. 一.问题引入 如下图所示,在x,y坐标轴上,我们绘制3个点A(1,1),B(2,0),C(2,3 ...

  9. Alpha阶段展示

    程序员杀产品经理祭天(SacrificePM)团队 1.团队成员简介和个人博客地址 故事 我们队伍的建立过程稍具戏剧性,大家看我们也颇为奇怪,这么一支8人队伍是怎么诞生的呢?其实我们原本分属三组,而第 ...

  10. PAT 1084 外观数列

    https://pintia.cn/problem-sets/994805260223102976/problems/994805260583813120 外观数列是指具有以下特点的整数序列: d, ...