已自我实现,但还是归入无码专区序列。哈哈哈哈哈

对于my idea部分,我的每一个想法都实现了,可供参考。


problem

给定一个 1∼n1\sim n1∼n 的排列和 kkk,求所有 r−l+1≥kr-l+1\ge kr−l+1≥k 的区间 [l,r][l,r][l,r] 中的第 kkk 大数,输出他们的和。

60%,n≤5e460\%,n\le 5e460%,n≤5e4

另 20%,k=120\%,k=120%,k=1

100%,n≤5e5,k≤50100\%,n\le 5e5,k\le 50100%,n≤5e5,k≤50

128MB,1s128MB,1s128MB,1s

my idea

注意到 kkk 是非常小的,粗略来看正解应该是 O(nk)O(nk)O(nk) 的。

考虑单独统计每个位置 iii 的贡献。

则需要枚举其左边有 xxx 个 >a[i]>a[i]>a[i] 的数字,右边有 k−x−1k-x-1k−x−1 个。因为 aia_iai​ 自己本身还要在前 kkk 大中占据一席位置。

然后需要知道两个端点的位置,记为 l,rl,rl,r。即 [l,i)[l,i)[l,i) 中 >a[i]>a[i]>a[i] 的数有 xxx 个,(i,r](i,r](i,r] 中 >a[i]>a[i]>a[i] 的数有 k−x−1k-x-1k−x−1 个。

假设 lll 左边,rrr 右边第一个 >a[i]>a[i]>a[i] 的数位置为 L,RL,RL,R。

则 ∀p∈(L,l],q∈[r,R)\forall\ p\in(L,l],q\in[r,R)∀ p∈(L,l],q∈[r,R) 的区间 [p,q][p,q][p,q] 的答案都会是 a[i]a[i]a[i]。

问题就在于怎么快速求得位置左右大于 a[i]a[i]a[i] 个数为 x(1≤x<k)x(1\le x<k)x(1≤x<k) 的所有位置。

没错——还是主席树!!!

弄两个主席树,一个表示前缀主席树,一个表示后缀主席树。

对 a[i]a[i]a[i] 建立权值线段树作下标,树上节点权值为下标 iii,维护最大值/最小值。

具体而言:以前缀树为例。

查询:查询前 i−1i-1i−1 个树的版本中 [ai+1,n][a_i+1,n][ai​+1,n] 的区间中的最大值。表示是最靠近 iii 的 >a[i]>a[i]>a[i] 的位置。

假设这个位置是 ppp,接下来就是在 p−1p-1p−1 的版本中查询 [ai+1,n][a_i+1,n][ai​+1,n] 的最大值,表示是第二靠近 iii 的 >a[i]>a[i]>a[i] 的位置,以此类推,最多查询到第 k−1k-1k−1 靠近就行了。

修改:直接在 i−1i-1i−1 的版本上新增将 a[i]a[i]a[i] 对应叶子权值修改为 iii,继续维护最大值。

后缀树与前缀树同理,从后往前做,维护最小值即可。

因为枚举顺序不一样所以是分开做,那么就可以共享同一棵主席树,节省空间。

将每个位置的左右都预处理出来,时间复杂度为 O(nklog⁡n)O(nk\log n)O(nklogn) 的。

对于 60%60\%60% 的数据点 n≤5e4n\le 5e4n≤5e4,算出来大约是 4e74e74e7 空间也是绰绰有余的。

只不过如果 n≤5e5n\le 5e5n≤5e5,超时不说,连主席树都开不出来这么大。

#include <bits/stdc++.h>
using namespace std;
#define maxk 52
#define maxn 50002
#define inf 0x3f3f3f3f
int n, k, cnt;
int lst[maxn][maxk], nxt[maxn][maxk];
int a[maxn], root[maxn];
struct node { int ans, lson, rson; } t[maxn * 25];#define mid ( ( l + r ) >> 1 )namespace pre_sgt {void build( int &now, int l, int r ) { t[now = ++ cnt].ans = 0;if( l == r ) return;build( t[now].lson, l, mid );build( t[now].rson, mid + 1, r );}void modify( int pre, int &now, int l, int r, int pos, int k ) {t[now = ++ cnt] = t[pre];if( l == r ) { t[now].ans = k; return; }if( pos <= mid ) modify( t[pre].lson, t[now].lson, l, mid, pos, k );else modify( t[pre].rson, t[now].rson, mid + 1, r, pos, k );t[now].ans = max( t[t[now].lson].ans, t[t[now].rson].ans );}int query( int now, int l, int r, int L ) {if( r < L ) return 0;if( L <= l ) return t[now].ans;return max( query( t[now].lson, l, mid, L ), query( t[now].rson, mid + 1, r, L ) );}
}namespace suf_sgt {void build( int &now, int l, int r ) { t[now = ++ cnt].ans = inf;if( l == r ) return;build( t[now].lson, l, mid );build( t[now].rson, mid + 1, r );}void modify( int pre, int &now, int l, int r, int pos, int k ) {t[now = ++ cnt] = t[pre];if( l == r ) { t[now].ans = k; return; }if( pos <= mid ) modify( t[pre].lson, t[now].lson, l, mid, pos, k );else modify( t[pre].rson, t[now].rson, mid + 1, r, pos, k );t[now].ans = min( t[t[now].lson].ans, t[t[now].rson].ans );}int query( int now, int l, int r, int L ) {if( r < L ) return inf;if( L <= l ) return t[now].ans;return min( query( t[now].lson, l, mid, L ), query( t[now].rson, mid + 1, r, L ) );}
}int main() {freopen( "kth.in", "r", stdin );freopen( "kth.out", "w", stdout );scanf( "%d %d", &n, &k );for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );pre_sgt :: build( root[0], 1, n );for( int i = 1;i <= n;i ++ ) {lst[i][0] = i;int pos = i - 1;for( int j = 1;j <= k;j ++ ) {lst[i][j] = pre_sgt :: query( root[pos], 1, n, a[i] + 1 );if( ! lst[i][j] ) break;else pos = lst[i][j] - 1;}pre_sgt :: modify( root[i - 1], root[i], 1, n, a[i], i );}cnt = 0;suf_sgt :: build( root[n + 1], 1, n );memset( nxt, 0x3f, sizeof( nxt ) );for( int i = n;i;i -- ) {nxt[i][0] = i;int pos = i + 1;for( int j = 1;j <= k;j ++ ) {nxt[i][j] = suf_sgt :: query( root[pos], 1, n, a[i] + 1 );if( nxt[i][j] == inf ) break;else pos = nxt[i][j] + 1;}suf_sgt :: modify( root[i + 1], root[i], 1, n, a[i], i );}long long ans = 0;for( int i = 1;i <= n;i ++ ) {for( int j = 0;j < k;j ++ ) {int l = lst[i][j], r = nxt[i][k - j - 1];if( ! l or r == inf ) continue;int posl = max( 1, lst[i][j + 1] + 1 );int posr = min( n, nxt[i][k - j] - 1 );ans += 1ll * ( l - posl + 1 ) * ( posr - r + 1 ) * 1ll * a[i];}}printf( "%lld\n", ans );return 0;
}

注意到还有 20%20\%20% 的额外特殊数据:k=1k=1k=1。

这就相当于求每个数为一个区间的最大值的贡献。

显然 [l,r][l,r][l,r] 中最大的数假设在 midmidmid 处,则会将区间划分成独立的两个子区间 [l,mid−1],[mid+1,r][l,mid-1],[mid+1,r][l,mid−1],[mid+1,r]。

所以可以直接递归处理,对于区间内最大值的贡献自然是 ∀l≤i≤mid≤j≤r\forall\ l\le i\le mid\le j\le r∀ l≤i≤mid≤j≤r 的 [i,j][i,j][i,j] 区间都合法。

一个区间内的最大值可以用 st表 预处理出来,查询做到 O(1)O(1)O(1)。

每个点就只会被当作最大值算一次,计算也是 O(1)O(1)O(1) 的。

时间复杂度在于 st表 的预处理,O(nlog⁡n)O(n\log n)O(nlogn)。

namespace K1 {long long ret = 0;int query( int l, int r ) {int i = log( r - l + 1 ) / log( 2 );if( a[st[l][i]] < a[st[r - ( 1 << i ) + 1][i]] ) return st[r - ( 1 << i ) + 1][i];else return st[l][i]; }void dfs( int l, int r ) {if( l > r ) return;if( l == r ) { ret += a[l]; return; }int mid = query( l, r );ret += 1ll * ( mid - l + 1 ) * ( r - mid + 1 ) * a[mid];dfs( l, mid - 1 );dfs( mid + 1, r );}void solve() {for( int i = 1;i <= n;i ++ ) st[i][0] = i;for( int j = 1;( 1 << j ) <= n;j ++ )for( int i = 1;i + ( 1 << j ) - 1 <= n;i ++ )if( a[st[i][j - 1]] > a[st[i + ( 1 << j - 1 )][j - 1]] )st[i][j] = st[i][j - 1];elsest[i][j] = st[i + ( 1 << j - 1 )][j - 1];dfs( 1, n );printf( "%lld\n", ret );}
}

将这两份代码拼凑在一起,就可以拿到 80′80'80′ 的高分了。

solution

事实上,我的想法已经非常接近正解了。

只是预处理不是采用主席树自带一个 log⁡\loglog ,而是使用双向链表。

把所有元素从大到小操作,用双向链表连接起来,每次新增一个数就会断掉两个位置的连接,然后加进去再连上。

链表 Li,RiL_i,R_iLi​,Ri​ 记录的是当前对于 iii 而言朝左朝右第一个比 a[i]a[i]a[i] 大的数为止,每次新增数都会维护这个链表。

暴力朝左朝右跳 kkk 次链表,将过程点记录下来,然后就可以枚举 xxx 计算了。

时间复杂度为 O(nk)O(nk)O(nk)。

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 500005
#define maxk 55
int n, k;
long long ans;
int lst[maxn], nxt[maxn], L[maxk], R[maxk], a[maxn], id[maxn];
set < int > s;int main() {freopen( "kth.in", "r", stdin );freopen( "kth.out", "w", stdout );scanf( "%d %d", &n, &k );for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] ), id[i] = i;sort( id + 1, id + n + 1, []( int x, int y ) { return a[x] > a[y]; } );for( int i = 1;i <= n;i ++ ) {int l = 0, r = 0, cnt_l = 0, cnt_r = 0, Last, Next;auto it = s.lower_bound( id[i] );if( it != s.end() ) r = *it, l = lst[r];else if( ! s.empty() ) l = *-- it, r = nxt[l];s.insert( id[i] );L[0] = R[0] = id[i], Last = l, Next = r;for( int j = 1;j <= k and l;j ++ ) L[++ cnt_l] = l, l = lst[l];for( int j = 1;j <= k and r;j ++ ) R[++ cnt_r] = r, r = nxt[r];if( cnt_l < k ) L[++ cnt_l] = 0;if( cnt_r < k ) R[++ cnt_r] = n + 1;for( l = k - 1, r = 0;~ l;l --, r ++ )if( l < cnt_l and r < cnt_r )ans += 1ll * ( L[l] - L[l + 1] ) * ( R[r + 1] - R[r] ) * a[id[i]];lst[id[i]] = Last, nxt[id[i]] = Next;if( Last ) nxt[Last] = id[i];if( Next ) lst[Next] = id[i];}printf( "%lld\n", ans );return 0;
}

【无码专区10】第K大查询(双向链表 /主席树+st表)相关推荐

  1. bzoj2588: Spoj 10628. Count on a tree(树上第k大)(主席树)

    每个节点继承父节点的树,则答案为query(root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)]]) #include<iostream> #i ...

  2. 【无码专区11】异或2(结论 / 推式子 + 哈希hash + 大整数高精度 加减乘除重载考察)

    本题已自我实现.但仍归于无码专区 problem 求 ∑i=1n−1i⨁(n−i)\sum_{i=1}^{n-1}i\bigoplus (n-i)∑i=1n−1​i⨁(n−i). 20%,n≤1e6; ...

  3. 【无码专区5】01串(大讨论+构造)

    因为只有std,没有自我实现,所以是无码专区 主要是为了训练思维能力 solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dl ...

  4. 【无码专区1】简单路径的第二大边权(启发式合并+最小生成树)

    只有std,没有自我实现,所以叫做无码专区 description 给一张无向图,多次询问,每次询问两个点之间所有简单路径(不重复经过点)中边权第二大(不是严格第二大)的权值的最小值. 数据范围:10 ...

  5. 【无码专区7】括号序列(思维)

    因为只有std,没有自我实现,所以是无码专区 主要是为了训练思维能力 solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dl ...

  6. 【无码专区6】球与盒子(数学线性筛)

    因为只有std,没有自我实现,所以是无码专区 主要是为了训练思维能力 solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dl ...

  7. 【无码专区13】最小公倍数(线段树)

    因为只有std,没有自我实现,所以是无码专区 主要是为了训练思维能力 my idea顾名思义,记录了我的整个思维过程,以及自己部分实现细节口胡,还有期望分数 solution才是dls正解,但是因为只 ...

  8. 【无码专区8】三角形二维数点——计数有多少个给定点落在三角形区域内

    因为只有std,没有自我实现,所以是无码专区 主要是为了训练思维能力 solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dl ...

  9. 【无码专区4】幸运数字4(折半搜索+计数+结论)

    因为只有std,没有自我实现,所以是无码专区 主要是为了训练思维能力 solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dl ...

最新文章

  1. jquery仿邮箱文本输入框自动加载邮箱后缀
  2. E - Right-Left Cipher CodeForces - 1087A (模拟)
  3. Oracle不使用索引的几种情况列举
  4. 好看的a标签按钮样式
  5. 如果把去掉数组里面重复的项
  6. Java基础—集合2Set接口和Map接口
  7. java键盘输入到文件中_在Linux中使用java和javac命令编译运行java文件
  8. mysql 开源订阅模式_Canal(增量数据订阅与消费 )快速配置
  9. 无聊的题目,权当一乐
  10. Java-杂项:Java数组Array和集合List、Set、Map
  11. Jetson TX2入门学习之Ubuntu默认密码
  12. 国内优秀论坛之大汇集
  13. Jupyter notebook切换python版本
  14. 向mysql中导入数据库文件
  15. 骑士人才系统4.0,5.0火车头数据抓取
  16. 数字后端设计流程小结
  17. 同花顺股票交易接口 正确用法
  18. [深度学习论文笔记][Adversarial Examples] Deep Neural Networks are Easily Fooled: High Confidence Predictions
  19. (转)四旋翼飞行器基本知识
  20. 我读《怪诞行为学2》

热门文章

  1. 中小学招生强化就近入学;首次全女性太空行走取消;苹果付费新闻APP奔溃;NASA火星上发现鹅卵石;这就是今天的大新闻...
  2. a*算法的时间复杂度_数据结构(1)——算法和时间复杂度
  3. mysql root密码忘记2018_2018-03-28设置及修改mysql用户密码学习笔记
  4. C语言阿斯码,木叶四位上忍设定各不相同,网红负责秀操作,她只需要美就够了...
  5. 怎么让图片手机上排列_荣耀手机系列档次怎么排列?
  6. php正则过滤html标签_空格_换行符的代码,PHP 正则过滤 html 标签、空格、换行符的代码 (文章格式化)...
  7. 各纬度气候分布图_欧洲气候特征:以温带气候类型为主,是海洋性气候最显著的大洲...
  8. 鸿蒙系统可以替代安卓吗,华为今天发布的鸿蒙系统,到底能不能替代安卓?
  9. php xlsx格式转换csv,我想使用C将.xls或.xlsx文件转换为.csv格式#
  10. 软件构造学习笔记-第二周