交错和查询

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. 类的__new__方法使用

    class Person(object): def __init__(self): self.name ="aaa" def defineName(self): self.name ...

  2. 一丶人生苦短,我用python【第一篇】

    1 解释器 解释器(英语:Interpreter),又译为直译器,是一种电脑程序,能够把高级编程语言一行一行直接转译运行.解释器不会一次把整个程序转译出来,只像一位"中间人",每次 ...

  3. linux常用命令补充详细

    1.ls命令 就是list的缩写,通过ls 命令不仅可以查看linux文件夹包含的文件,而且可以查看文件权限(包括目录.文件夹.文件权限)查看目录信息等等 常用参数搭配: ls -a 列出目录所有文 ...

  4. LAXCUS对数据存储的优化

        LAXCUS兼容行存储(NSM)和列存储(DSM)两种数据模型,实现了混合存储.同时在分布环境里,做到将数据的分发和备份自动处理,这样就不再需要人工干预了.     行存储,为了兼容广大用户对 ...

  5. Python图像全屏显示

    需要在嵌入式设备上全屏显示图像,使用pil显示图像时,只能通过系统的图像浏览器显示.所以使用Python自带的tkinter import Tkinter as tk   这句在Python3中已经改 ...

  6. 爬虫:Scrapy17 - Common Practices

    在脚本中运行 Scrapy 除了常用的 scrapy crawl 来启动 Scrapy,也可以使用 API 在脚本中启动 Scrapy. 需要注意的是,Scrapy 是在 Twisted 异步网络库上 ...

  7. 软件工程项目组Z.XML会议记录 2013/10/22

    软件工程项目组Z.XML会议记录 [例会时间]2013年10月22日星期二21:00-22:30 [例会形式]小组讨论 [例会地点]三号公寓楼会客厅 [例会主持]李孟 [会议记录]周敏轩 会议整体流程 ...

  8. Daily Scrum02 11.29

    今天大家都已经开始了进行第二轮迭代的工作!相比第一轮迭代,每个人都已经有了一定开发经验,这次做起来顺手很多.薛神和肖犇的挑战最大,他们需要实现好友功能,手机间的通信.服务器的搭建都是难点,但他们的热情 ...

  9. grpc deadlines

    最近在将应用的rpc更换为grpc,使用过程中,发现报“rpc error:code=DeadlineExceeded desc = context deadline exceeded”,这是啥?原来 ...

  10. 【bzoj3033】太鼓达人 DFS欧拉图

    题目描述 给出一个整数K,求一个最大的M,使得存在一个每个位置都是0或1的圈,圈上所有连续K位构成的二进制数两两不同.输出最大的M以及这种情况下字典序最小的方案. 输入 一个整数K. 输出 一个整数M ...