[loj3697]262144 Revisited

cntv表示答案v的区间数量,则问题即求v1cntv

fl表示最大的右端点r满足区间[l,r)的答案<v,则cntv=(n+12)l=1n(fll)

初始v=1fl=l,当vv+1时转移即fl={l+1al=vfflalv,时间复杂度为o(n2)

结论1:对于所有v,答案为v的极长区间数之和为o(nlogn)

fnn时的答案,归纳证明fno(logn!)o(nlogn)

ap为最大值,则fn=fp1+fnp+包含p的极长区间数

在确定左端点后l后,显然[l,n]的答案[l,p]的答案+o(lognp),因此至多o(lognp)个极长区间​

对于所有o(p)个左端点,包含p的极长区间数o(plognp)o(i=0p1lognipi)=o(logn!p!(np)!)

代入原式显然成立,即得证

结合上述结论,考虑以下优化——

优化1:仅维护所有极长且非空的区间[l,fl),由于f单调不降,条件即max(l,fl1)<fl

在此基础上,转移分为两步:

1.将区间[l,fl)替换为[l,fl),其中ffl可以双指针求出(注意判断[l,fl)是否极长)

2.对于al=v的位置,新增区间[l,l+1),可以用归并排序与前者合并

优化2:min(al1,afl)v,则fl=fl(转移不影响),将其单独存储在lfl

显然每一个位置至多存储一个,并在al=v时将ll+1上存储的区间重新维护即可

结论2:上述做法的时间复杂度为o(nlogn)

不难发现,所有维护/存储(包括优化2)的区间[l,fl)即答案<v的极长区间

关于转移的第1步,对[l,fl)是否极长分类讨论:

1.若不极长,即使得区间个数-1,可以均摊到加入区间时

2.若极长,结合min(al1,afl)<v,在[l1,fl)[l,fl]中总有一个答案v

两者均包含[l,fl),因此[l,fl)极长的必要条件为fl<fl,进而[l,fl)的答案恰为v(结合f定义)

进一步的,[l,fl)必然是答案为v的极长区间,根据结论1总复杂度至多为o(nlogn)

另外,转移的第2步和优化2存储的复杂度显然均为o(n),即得证

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 300000
 4 #define M 1000100
 5 #define ll long long
 6 #define pii pair<int,int>
 7 #define mp make_pair
 8 #define fi first
 9 #define se second
10 int n,a[N];ll sum,ans;pii f0[N];
11 vector<int>v[M];vector<pii>g,f;
12 void add(pii k){
13     f0[k.fi]=f0[k.se]=k;
14     sum+=(ll)(k.se-k.fi)*(k.se-k.fi+1)/2;
15 }
16 void dec(pii k){
17     f0[k.fi]=f0[k.se]=mp(0,0);
18     sum-=(ll)(k.se-k.fi)*(k.se-k.fi+1)/2;
19 }
20 int main(){
21     scanf("%d",&n);
22     for(int i=1;i<=n;i++){
23         scanf("%d",&a[i]);
24         v[a[i]].push_back(i);
25     }
26     for(int i=1;i<M;i++){
27         g.clear(),ans+=(ll)n*(n+1)/2-sum;
28         for(int x=0;x<f.size();x++){
29             int s=min(f[x].se,(x+1<f.size() ? f[x+1].fi-1 : n));
30             ans-=(ll)((f[x].se-s)+(f[x].se-f[x].fi))*(s-f[x].fi+1)/2;
31         }
32         for(int x=0,y=0;x<f.size();x++){
33             while ((y+1<f.size())&&(f[y+1].fi<=f[x].se))y++;
34             if ((g.empty())||(g.back().se<f[y].se))g.push_back(mp(f[x].fi,f[y].se));
35         }
36         f.clear();
37         for(int x=0,y=0;x<=g.size();x++){
38             int s=(x<g.size() ? g[x].fi : n+1);
39             while ((y<v[i].size())&&(v[i][y]<s)){
40                 int k=v[i][y];
41                 if (f0[k].fi)f.push_back(f0[k]),dec(f0[k]);
42                 f.push_back(mp(k,k+1));
43                 if (f0[k+1].fi)f.push_back(f0[k+1]),dec(f0[k+1]);
44                 y++;
45             }
46             if (x<g.size()){
47                 bool flag=0;
48                 if ((g[x].fi>1)&&(a[g[x].fi-1]<=i))flag=1;
49                 if ((g[x].se<=n)&&(a[g[x].se]<=i))flag=1;
50                 if (!flag)add(g[x]);
51                 else f.push_back(g[x]);
52             }
53         }
54     }
55     printf("%lld\n",ans);
56     return 0;
57 }
View Code
复制代码

 

posted @   PYWBKTDA  阅读(216)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2021-04-16 [loj3501]图函数
2021-04-16 [loj3503]滚榜
2021-04-16 [loj3500]矩阵游戏
点击右上角即可分享
微信分享提示