• 周四. 8月 11th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

[hdu7020]Array

admin

11月 28, 2021

(这是一个线性的做法)

显然对于合法的区间,众数是唯一的,因此不妨枚举众数,将众数标记为1、其余数标记为-1,此时问题即求有多少个区间和大于0

考虑暴力的做法:从左到右枚举右端点,记当前前缀和为$tot$​​,即查询之前有多少个前缀和小于$tot$​​​

具体的,记$f_{i}$​​​为(当前)有多少个前缀和为$i$​​​​,即查询$sum_{i=min }^{tot-1}f_{i}$​​​,并将$f_{tot}$​​​加1​

(其中$min$为历史前缀最小值,初始为$f_{0}=1$且$forall i
e 0,f_{i}=0$)

由于$tot$​​的变化是连续的,不难线性维护$sum_{i=min}^{tot-1}f_{i}$​​,时间复杂度为$o(n^{2})$​​

进一步的,注意到1的个数和是$o(n)$​的,-1的段数和也是$o(n)$​的,因此将连续的一段-1一起处理

具体的,考虑处理一段-1,假设这段-1之前的前缀和为$tot$​​​,这段-1的长度为$l$​​​,即查询$sum_{i=tot-l}^{tot-1}sum_{j=min}^{i-1}f_{j}$​​​,并将$forall tot-lle i<tot,f_{i}$​​​​加1

对于修改,可以通过差分维护,并记差分数组为$Delta f_{i}$​​(定义$f_{i}=sum_{j=min}^{i}Delta f_{j}$​​​)

对于查询,不妨暴力枚举$tot$,同样利用$tot$变化的连续性,线性维护$sum_{j=min}^{tot}Delta f_{j}$和$sum_{j=min}^{tot-1}f_{j}$​,那么不难得到查询的结果($tot$暴力枚举即代替了$i$),但时间复杂度仍为$o(n^{2})$

考虑$min$​,显然当$totle min$时之后的部分必然都为0,不妨直接变为最终的$tot-l$​(注意更新$min$)

考虑此时的复杂度,注意到每一次$mn$减少,都会减少对应$tot$的枚举,而最终$mn$至多为所有元素和,因此$tot$的枚举级别是1的个数,也即$o(n)$的

对于单个1直接暴力”枚举”$tot$即可,显然是$o(n)$的

最终,总复杂度为$o(n)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 1000005
 4 #define ll long long
 5 stack<int>st;
 6 vector<int>v[N];
 7 int t,n,m,x,now,mn,cnt,s,f[N<<1];
 8 ll ans;
 9 void update(int l,int r){
10     f[l]++,f[r+1]--;
11     st.push(l),st.push(r+1); 
12     cnt++;
13 }
14 void dec(int x){
15     for(int i=0;i<x;i++){
16         if (now==mn){
17             now=mn=now-(x-i);
18             cnt=s=0;
19             return;
20         }
21         cnt-=f[now--],s-=cnt,ans+=s;
22     }
23 }
24 int main(){
25     scanf("%d",&t);
26     while (t--){
27         scanf("%d",&n);
28         m=1e6,ans=0;
29         for(int i=0;i<=m;i++)v[i].clear();
30         for(int i=1;i<=n;i++){
31             scanf("%d",&x);
32             v[x].push_back(i);
33         }
34         for(int i=0;i<=m;i++){
35             int lst=0;
36             now=mn=n,cnt=s=0;
37             update(now,now);
38             //cnt=sum_{i=mn}^{tot}f_{i},s=sum_{i=mn}^{tot-1}sum_{j=mn}^{i}f_{j} 
39             for(int j=0;j<v[i].size();j++){
40                 if (lst+1<v[i][j]){
41                     dec(v[i][j]-lst-1);
42                     update(now,now+(v[i][j]-lst-1)-1);
43                 }
44                 s+=cnt,cnt+=f[++now],ans+=s;
45                 update(now,now);
46                 lst=v[i][j];
47             }
48             if (lst<n)dec(n-lst);
49             while (!st.empty()){
50                 f[st.top()]=0;
51                 st.pop();
52             }
53         }
54         printf("%lld
",ans);
55     }
56     return 0;
57 }

View Code

发表评论

您的电子邮箱地址不会被公开。