网络赛牡丹江赛区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个结点 ...
随机推荐
- mysql 查看表信息
desc 表名; 查看表结构信息 show create table 表名; 查询建表详细信息 select COLUMN_NAME,COLUMN_TYPE,COLUMN_COMMENT from i ...
- 设计模式--策略模式C++实现
策略模式C++实现 1定义 (Strategy Pattern)定义一组算法,将每个算法都封装起来,并且使他们可以相互替换 也叫政策模式 2类图 3实现 class Strategy{ protect ...
- mysql中的tinyint在C#中的类型
mysql中的tinyint在C#中的类型 在C#中对应的类型是System.SByte,不是byte.
- poj2195
题解: 简单KM 把每一个男的和房子分离 代码: #include<cstdio> #include<cmath> #include<algorithm> #inc ...
- OpenMP学习
当数据量较大或者时间复杂度高的时候,盲目地等待结果,开发效率极低. 听说OpenMP可以多核并行运算,加快运行速度.整理了一些OpenMP的资料,以待学习. 1.openMP的一点使用经验 2.Ope ...
- idea git tag 管理
项目release 之后一般都会打一个tag 做记录.本人使用idea管理tag的时候,遇到的问题做一些记录. 1:idea 创建tag idea 创建tag ,我们可以右键项目,然后按照下图操作创建 ...
- New Concept English three(17)
27W/m 65 Verrazano, an Italian about whom little is known, sailed into New York Harbour in 1524 and ...
- iscroll中文文档
转自:http://blog.csdn.net/sweetsuzyhyf/article/details/44195549 IScroll.js 最新版本 v5.1.2 修复了输入框无法输入和横向滚动 ...
- vue.js 源代码学习笔记 ----- $watcher
/* @flow */ import { queueWatcher } from './scheduler' import Dep, { pushTarget, popTarget } from '. ...
- js跨域详解
跨域概念:Cross-origin resource sharing