problem

luogu-P3246

心路历程+卡常历程+问题存疑

一直在想莫队的做法。发现左右指针的移动对应一段左/右端点固定的子序列,然后可以一个数代表一段相同的贡献。

就开始求 lsti,nxtilst_i,nxt_ilsti​,nxti​ 了。

仔细想想需要找到 lstlsti<l≤lstilst_{lst_i}<l\le lst_ilstlsti​​<l≤lsti​ 的特殊位置,然后要知道这中间点的贡献。

我就在来了个线段树维护区间和,然后用 set\text{set}set 存 lstilst_ilsti​ 二分找。

后来半天过不了样例,不是过大就是过小。

隐约间觉得不对劲,后来真的好像不对劲,我就大刀阔斧直接砍了。

开始怀疑自己的莫队算法。此时经过了八点半到九点十分共四十多分钟的时间。

我开始尝试整体二分,后面发现自己完全不会分治。此时我陷入了困境。

我又倒回去想了下莫队,我发现问题出在 lstilst_ilsti​ 中间不是所有点都是对应在 [l,r][l,r][l,r] 的贡献,我好像算多了。

因为 lstilst_ilsti​ 是从 iii 出发得到的,而非整体的 rrr。

难道我只用维护一个全局的?不,这样会算少。

突然我意识到可以把这种关系建成树!并且发现了编号之间与子序列间的对应关系。

此时时间大概在酒店四五十左右,我就开始敲了。

过程比较顺利,十点半不到?就敲完了。然后开始调,输出中间结果,发现最后结果已经和样例快要吻合时,我就更加坚定了自己的做法。

十一点我通过了小样例,紧接着大样例也是正确的。

但很遗憾,耗时 3.6s3.6s3.6s 多。

我发现输入数据过多,就换成了快读快输。耗时变成了 3.0s3.0s3.0s 多。

接着我觉得倍增对于极限数据也只会到 161616 ,所以卡了下倍增的循环。耗时缩减为 2.6s2.6s2.6s。

改下块长加 O2O2O2 就能看看卡过。

但是当时我尝试了一下用莫队的奇偶优化,时间瞬间到了 1.9s1.9s1.9s 左右。

此时出现了个大问题!我发现这样就跑不过大样例了。

我对此非常疑惑。

下午又研究了一番,发现当我将莫队的四个指针移动前后两个交换位置,也会跑不过大样例?!!莫队询问排序块内按右端点降序排列也不行!

这下真给我整不会了,我突然猜是不是爆 long long 了,用 assert\text{assert}assert 发现没有,自己算极限也是 6e186e186e18 不到。

后面通过数据,我觉得应该是倍增到最后不能往上跳部分出现了问题。

但是这样怎么可能会让我的第一次建构代码跑过去呢?

至今我仍然不清楚。

只能说非常幸运的在第一次敲代码的时候就跑过去了把。

如果有人知道这是为什么的话,麻烦请联系一下我!谢谢了。

solution

由于昨天做的一道题题解提了一句莫队二次离线,形式就是 l≤x≤y≤rl\le x\le y\le rl≤x≤y≤r。

所以一看到这个题,我就在想莫队的做法。

莫队反正就是左端点分块排序,右端点块内递增,就两个指针移动的板子,时间复杂度就带了个 n\sqrt{n}n​。

问题是如何快速计算移动 l/rl/rl/r 一个位置后对应的答案动态变化。

以右移 rrr 为例,当 r←r+1r\leftarrow r+1r←r+1的时候,我们需要新加 [l,r]∼r+1[l,r]\sim r+1[l,r]∼r+1 共 r−l+1r-l+1r−l+1 组新的子序列贡献。

然后发现,我们只需要跳较小值的位置即可。

具体而言,假设 ppp 满足 ap≤ar+1∧∀p<i<r+1ai>ar+1a_p\le a_{r+1}\wedge \forall_{p<i<r+1}\ a_i>a_{r+1}ap​≤ar+1​∧∀p<i<r+1​ ai​>ar+1​。

最大的不超过 ar+1a_{r+1}ar+1​ 的位置,记为 lstr+1lst_{r+1}lstr+1​。

则对于 [p,r]∼r+1[p,r]\sim r+1[p,r]∼r+1 共 r−p+1r-p+1r−p+1 组子序列的贡献都是 apa_pap​。

同理我们找到 lstplst_plstp​,然后共 p−lstp+1p-lst_p+1p−lstp​+1 组子序列的贡献都是 alstpa_{lst_p}alstp​​。

以此类推…\dots…直到跳到 <l<l<l 的位置,lll 开头那一段贡献可能不完整,简单处理一下即可。

我们肯定不能暴力跳,注意到一个数对应的完整区间贡献是固定的,所以我们可以倍增!

我们将 lsti→ilst_i\rightarrow ilsti​→i 建立有向边,然后生成一棵树,不难发现。(u−fa)∗au(u-fa)*a_u(u−fa)∗au​ 即为 uuu 对应的完整贡献。

至于找 lstilst_ilsti​,答主比较无脑,直接权值线段树上,完全不带脑子的大常数。而且 aia_iai​ 值域过大,还加了个离散化。

rrr 左移减去贡献与上面的过程完全一样。

而 lll 的左移右移则需要维护 nxtinxt_inxti​,最小的不大于 aia_iai​ 的位置。获得方式同 lstilst_ilsti​。

同样 nxti→inxt_i\rightarrow inxti​→i 建立有向边,生成一棵树。

为了使得点与父节点之间能算出点的贡献,我们钦定找不到的 lsti=0,nxti=n+1lst_i=0,nxt_i=n+1lsti​=0,nxti​=n+1。

时间复杂度大概是 O(nnlog⁡n)O(n\sqrt n\log n)O(nn​logn)。

会被卡,那就稍微调整一下块长。

code

#include <bits/stdc++.h>
using namespace std;
#define lson now << 1
#define rson now << 1 | 1
#define mid  (l + r >> 1)
#define ll long long
#define maxn 100005
int n, Q, B; ll ans;
int a[maxn], b[maxn], block[maxn], lst[maxn], nxt[maxn];
ll ret[maxn];
struct query { int l, r, id; }q[maxn];namespace MaxSgt {int Max[maxn << 2];void modify( int now, int l, int r, int p, int v ) {if( l == r ) { Max[now] = v; return; }if( p <= mid ) modify( lson, l, mid, p, v );else modify( rson, mid + 1, r, p, v );Max[now] = max( Max[lson], Max[rson] );}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return Max[now];return max( query( lson, l, mid, L, R ), query( rson, mid + 1, r, L, R ) );}
}namespace MinSgt {int Min[maxn << 2];void build( int now, int l, int r ) {Min[now] = n + 1;if( l == r ) return;build( lson, l, mid );build( rson, mid + 1, r );}void modify( int now, int l, int r, int p, int v ) {if( l == r ) { Min[now] = v; return; }if( p <= mid ) modify( lson, l, mid, p, v );else modify( rson, mid + 1, r, p, v );Min[now] = min( Min[lson], Min[rson] );}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return n + 1;if( L <= l and r <= R ) return Min[now];return min( query( lson, l, mid, L, R ), query( rson, mid + 1, r, L, R ) );}
}struct tree {vector < int > G[maxn];int f[maxn][17]; ll g[maxn][17];void addedge( int u, int v ) {G[u].push_back( v );}int Fabs( int x ) {return x < 0 ? -x : x;}void dfs( int u, int fa ) {f[u][0] = fa, g[u][0] = 1ll * Fabs( u - fa ) * b[a[u]]; for( int i = 1;i < 17;i ++ ) {f[u][i] = f[f[u][i - 1]][i - 1];g[u][i] = g[f[u][i - 1]][i - 1] + g[u][i - 1];}for( int i = 0;i < G[u].size();i ++ ) dfs( G[u][i], u );}
}L, R;void AddR( int l, int r ) {for( int i = 16;~ i;i -- )if( L.f[r][i] >= l ) ans += L.g[r][i], r = L.f[r][i];ans += 1ll * (r - l + 1) * b[a[r]];
}void SubR( int l, int r ) {for( int i = 16;~ i;i -- )if( L.f[r][i] >= l ) ans -= L.g[r][i], r = L.f[r][i];ans -= 1ll * (r - l + 1) * b[a[r]];
} void AddL( int l, int r ) {for( int i = 16;~ i;i -- )if( R.f[l][i] <= r ) ans += R.g[l][i], l = R.f[l][i];ans += 1ll * (r - l + 1) * b[a[l]];
}void SubL( int l, int r ) {for( int i = 16;~ i;i -- )if( R.f[l][i] <= r ) ans -= R.g[l][i], l = R.f[l][i];ans -= 1ll * (r - l + 1) * b[a[l]];
}void read( int &x ) {x = 0; int f = 1; char s = getchar();while( s < '0' or s > '9' ) {if( s == '-' ) f = -1; s = getchar();}while( '0' <= s and s <= '9' ) {x = (x << 1) + (x << 3) + (s ^ 48);s = getchar();}x *= f;
}void print( ll x ) {if( x < 0 ) putchar('-'), x = -x;if( x > 9 ) print( x / 10 );putchar( x % 10 + '0' );
}int main() {read( n ), read( Q );B = 400;for( int i = 1;i <= n;i ++ ) read( a[i] );for( int i = 1;i <= Q;i ++ ) read( q[i].l ), read( q[i].r ), q[i].id = i;for( int i = 1;i <= n;i ++ ) block[i] = (i - 1) / B + 1;sort( q + 1, q + Q + 1, []( query x, query y ) { return (block[x.l] == block[y.l]) ? (x.r < y.r) : (x.l < y.l); } );for( int i = 1;i <= n;i ++ ) b[i] = a[i];sort( b + 1, b + n + 1 );int m = unique( b + 1, b + n + 1 ) - b - 1;for( int i = 1;i <= n;i ++ ) a[i] = lower_bound( b + 1, b + m + 1, a[i] ) - b;for( int i = 1;i <= n;i ++ ) {lst[i] = MaxSgt :: query( 1, 1, m, 1, a[i] );MaxSgt :: modify( 1, 1, m, a[i], i );L.addedge( lst[i], i );}MinSgt :: build( 1, 1, m );for( int i = n;i >= 1;i -- ) {nxt[i] = MinSgt :: query( 1, 1, m, 1, a[i] );MinSgt :: modify( 1, 1, m, a[i], i );R.addedge( nxt[i], i );}L.dfs( 0, 0 );R.dfs( n + 1, n + 1 );int curl = 1, curr = 0;for( int i = 1;i <= Q;i ++ ) {int l = q[i].l, r = q[i].r;while( curr < r ) AddR( curl, ++ curr );while( curr > r ) SubR( curl, curr -- );while( curl > l ) AddL( -- curl, curr );while( curl < l ) SubL( curl ++, curr );ret[q[i].id] = ans;}for( int i = 1;i <= Q;i ++ ) print( ret[i] ), putchar('\n');return 0;
}

[HNOI2016] 序列(线段树 + 莫队 + 倍增)相关推荐

  1. 牛客网多校联合训练1 J Different Integers(可持久化线段树/莫队)

    题意:给你一个L,R区间,让你求1-L和R-n中有多少个数 思路:最美不过夕阳红,一直没想到拼接起来,之前想到拼接,但想的是每次都进行拼接,所以是一个n2lg的操作,最后想到了拼接全部,然后就是一个经 ...

  2. 【CodeForces】700 D. Huffman Coding on Segment 哈夫曼树+莫队+分块

    [题目]D. Huffman Coding on Segment [题意]给定n个数字,m次询问区间[l,r]的数字的哈夫曼编码总长.1<=n,m,ai<=10^5. [算法]哈夫曼树+莫 ...

  3. codeforces 877E. Danil and a Part-time Job (DFS序列+线段树)

    传送门:codeforces 877E 题目大意: 有一颗树,树的每个节点有一盏灯,状态为亮或灭.现在可以进行以下两种操作: 1.pow x,将以 x 为根节点的子树(包括根节点)的所有节点的灯的状态 ...

  4. HDU6964 I love counting (字典树+莫队)

    题意: 给定一个长度为 nnn 的序列c,qc,qc,q 次询问,每次给出l,r,a,bl,r,a,bl,r,a,b求在[l,r][l,r][l,r]中有多少种不同的值 kkk 满足 k⊕a≤b​.k ...

  5. P3180-[HAOI2016]地图【圆方树,莫队,分块】

    正题 题目链接:https://www.luogu.com.cn/problem/P3180 题目大意 nnn个点mmm条边的一个仙人掌,有点权. QQQ次询问给出op,x,yop,x,yop,x,y ...

  6. bzoj4564: [Haoi2016]地图 仙人掌的圆方树 莫队 分块

    bzoj4564: [Haoi2016]地图 Description 一天rin来到了一个遥远的都市.这个都市有n个建筑,编号从1到n,其中市中心编号为1,这个都市有m条双向通 行的街道,每条街道连接 ...

  7. 主席树 | 莫队 ---- Codeforces Round #716 (Div. 2) D. Cut and Stick [主席树or莫队优化] 区间众数问题(静态)

    题目链接 题目大意: 就是给你nnn个数,和q次询问,每次询问给你一个区间[l,r][l,r][l,r],问你把区间里面的数分配成最少多少块,使得块内出现最多次数的数不超过区间长度的一半(除不尽向上取 ...

  8. 【线段树】[LUOGU 守墓人] [LUOGU 维护序列] 线段树模板题

    题目: 题目链接:[LUOGU 守墓人] 题解: 线段树单点修改,区间修改,单点查询,区间查询,一系列线段树基本操作,模板打就好. (回头再补一个分块和树状数组的这种板子题,就是用分块和树状数组再写一 ...

  9. BZOJ1798 【AHOI2009】 seq维护序列 线段树

    维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把 ...

最新文章

  1. 如何理解jdk8通过行为参数化传递代码
  2. 吊打一切现有开源OCR项目!90% +准确率,训练部署一条龙
  3. Qt Creator图片
  4. 【Hibernate】Hibernate实体关系映射——主键相同的一对一关系
  5. 电话光端机与PCM复用设备的区别
  6. markdown 入门1--标题目录代码图片
  7. FTP/文件传输协议
  8. 移动边缘计算卸载技术笔记(一)
  9. Unity中录制VR全景视频(可录制UGUI)
  10. Euclid 欧几里得算法(c语言递归版)
  11. 风格迁移应用_浅谈风格迁移(一)固定风格迁移
  12. 骑士旅行问题(骑士走棋盘)
  13. 进制详解:二进制、八进制和十六进制
  14. Linux基础命令与知识点
  15. Fiddler 抓包下载 M3U8 视频
  16. 推荐6款小众实用的Mac软件,让人耳目一新!
  17. 实时语音通讯的回音消除技术详解
  18. discuz!内置代码
  19. 冯诺依曼机核心由运算器转变为存储器的原因
  20. 修改计算机名后导致oracle的oem(企业管理器)无法使用

热门文章

  1. 掌握神经网络,入门深度学习
  2. 每日一笑 | 男朋友整天沉迷游戏怎么办...?
  3. AI催生新的工作机遇:5个未来会很吃香的岗位
  4. 盘点那些让程序员目瞪口呆的Bug都有什么?
  5. python爬虫登录有验证码_大神教你用Python爬虫模拟登录带验证码网站
  6. linux脚本开机挂载,案例七:shell实现开机自动挂载本地YUM仓库程序
  7. 针对Spring的Spring Retry 我发现了这样一个大家都不知道的技巧!
  8. jq动态获取input的值传给html,jquery 保证html()拿到的html字符串包含input的value值
  9. java 线程访问控件_C#多线程与跨线程访问界面控件的方法
  10. python编译安装没有c扩展_为什么在安装simplejson时得到“C扩展无法编译”?