PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 P8313 [COCI2021-2022#4] Izbori
Malnar 先生正在竞选县长,这个县一共有 n n n 栋房屋,每栋房屋里都住着一位居民。Malnar 先生知道,选举的赢家不一定是最好的候选人,而是在选举前举办的宴会最好的候选人。因此,在选举前几天,他将邀请第 l l l 至 r ( l ≤ r ) r(l\le r) r(l≤r) 栋房屋内居住的居民,为他们准备一顿丰盛的晚餐。
Malnar 先生知道所有居民最喜欢吃的菜。在宴会上,他会准备大多数人喜欢的一道菜。如果一个人吃到了自己最喜欢吃的菜,将会投一票给 Malnar 先生。但是如果没有吃到自己最喜欢吃的菜,他们将会把票投给 Vlado 先生。如果没有来参加晚宴的居民,他们将会忘记选举,不做出任何投票。如果一个候选人获得了投了票的人中一半以上的人的支持,他将会赢得竞选。
Malnar 先生想知道,有多少组的 ( l , r ) (l,r) (l,r) 可以使他赢得竞选。
第一行包含一个整数 n n n,表示房屋的数量。
第二行包含 n n n 个整数 a i a_i ai,表示第 i i i 栋房屋内居民最喜欢的菜。
仅一行,输出可以使 Malnar 先生赢得竞选的 ( l , r ) (l,r) (l,r) 的组数。
2
1 1
3
3
2 1 2
4
5
2 2 1 2 3
10
【样例 2 解释】
可以使 Malnar 先生赢得竞选的 ( l , r ) (l,r) (l,r) 为: ( 1 , 1 ) , ( 2 , 2 ) , ( 3 , 3 ) , ( 1 , 3 ) (1, 1),(2, 2),(3, 3),(1, 3) (1,1),(2,2),(3,3),(1,3)。
【数据规模与约定】
本题采用子任务捆绑测试。
对于 100 % 100\% 100% 的数据, 1 ≤ l ≤ r ≤ n ≤ 2 × 1 0 5 , 1 ≤ a i ≤ 1 0 9 1 \le l\le r ≤ n ≤ 2\times10^5,1 ≤ a_i ≤ 10^9 1≤l≤r≤n≤2×105,1≤ai≤109。
【提示与说明】
本题分值按 COCI 原题设置,满分 110 110 110。
题目译自 COCI2021-2022 CONTEST #4 T3 Izbori。
O
(
n
3
)
O(n^3)
O(n3)的暴力
直接暴力累加计算就好了。
但其实累加的时候会有一个想法:
s
u
m
j
−
(
j
>
>
1
)
>
s
u
m
i
−
1
−
(
(
i
−
1
)
>
>
1
)
sum_{j}-(j>>1)>sum_{i-1}-((i-1)>>1)
sumj−(j>>1)>sumi−1−((i−1)>>1)
即
2
×
s
u
m
j
−
j
>
2
×
s
u
m
i
−
1
−
i
−
1
即2\times sum_{j}-j>2\times sum_{i-1}-i-1
即2×sumj−j>2×sumi−1−i−1
这就推出了我们的正解的想法。
我们再用一个数组
c
c
c来保存
2
×
s
u
m
i
−
i
2\times sum_i-i
2×sumi−i的值。
c
i
=
2
×
s
u
m
i
−
i
c_i=2\times sum_i-i
ci=2×sumi−i
因为事实上我们只需要
c
c
c的相对大小,并且每一段的
c
c
c都是一段公差为
1
1
1的等差数列,所以可以用类似于桶的想法,用一个数组
d
d
d来保存不同值的
c
c
c的个数。
d
i
=
Σ
j
=
1
i
c
j
d_i=\Sigma_{j=1}^ic_j
di=Σj=1icj
于是我们就又会发现,对于一段
c
c
c,产生的贡献又是对应这一段的
Σ
d
i
\Sigma d_i
Σdi,所以可以再用数组
e
e
e保存数组
d
d
d的前缀和。
e
i
=
Σ
j
=
1
i
d
j
e_i=\Sigma_ {j=1}^id_j
ei=Σj=1idj
同时我们需要进行区间修改并求前缀和,所以我们又可以将其改为单点修改并求前缀和,此时,我们还需要一个数组
f
f
f。
总结一下上面的数组:(分不同种类的数)
s
u
m
j
=
Σ
i
=
1
j
a
i
sum_j=\Sigma_{i=1}^ja_i
sumj=Σi=1jai
c
j
=
2
×
s
u
m
j
−
j
c_j=2\times sum_j-j
cj=2×sumj−j
d
k
=
Σ
j
=
1
n
(
c
j
=
=
k
)
(需要被区间修改)
d_k=\Sigma_{j=1}^n(c_j==k)(需要被区间修改)
dk=Σj=1n(cj==k)(需要被区间修改)
e
i
=
Σ
k
=
1
i
d
k
e_i=\Sigma_{k=1}^id_k
ei=Σk=1idk
f
l
=
Σ
l
=
1
i
e
l
f_l=\Sigma_{l=1}^ie_l
fl=Σl=1iel
所以维护三个树状数组就行。
//洛谷 P8313 [COCI2021-2022#4] Izbori
#pragma GCC optimize(3)
#include
#include
#include
#include
using namespace std;
int n;
int a[200010];
int b[200010];
int sum[200010];
int cnt;
int x;
long long ans;
int find(int x){//二分查找
int l=0,r=cnt,mid;
while(l<r){
mid=(l+r+1)>>1;
if(b[mid]<=x){
l=mid;
}
else{
r=mid-1;
}
}
return l;
}
void in(int &x){
int nt;
x=0;
while(!isdigit(nt=getchar()));
x=nt^'0';
while(isdigit(nt=getchar())){
x=(x<<3)+(x<<1)+(nt^'0');
}
}
int main(){
register int i,j;
in(n);
for(i=1;i<=n;++i){
in(a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-(b+1);//离散化
for(i=1;i<=n;++i){
x=find(a[i]);
++sum[x];
++ans;
for(j=i+1;j<=n;++j){
x=++sum[find(a[j])]>sum[x]?find(a[j]):x;
if(sum[x]>=((j-i+1)>>1)+1){
++ans;
}
}
memset(sum,0,sizeof(sum));
}
printf("%lld\n",ans);
return 0;
}
//洛谷 P8313 [COCI2021-2022#4] Izbori
#pragma GCC optimize(3)
#include
#include
#include
#include
#include
using namespace std;
int n;
int a[200010];
int b[200010];//用于离散化
long long d[400010];
long long e[400010];
long long f[400010];
int cnt;
int x,y;
long long ans;
vector<int>t[200010];
int lowbit(int x){
return x&-x;
}
int find(int x){
int l=0,r=cnt,mid;
while(l<r){
mid=(l+r+1)>>1;
if(b[mid]<=x){
l=mid;
}
else{
r=mid-1;
}
}
return l;
}
void in(int &x){
int nt;
x=0;
while(!isdigit(nt=getchar()));
x=nt^'0';
while(isdigit(nt=getchar())){
x=(x<<3)+(x<<1)+(nt^'0');
}
}
void add(int i,long long c){
int x=i;
while(i<=2*n+2){
d[i]+=c;
e[i]+=c*x;
f[i]+=c*x*x;
i+=lowbit(i);
}
}
long long query(int i){
long long ans=0;
int x=i;
while(i){
ans+=d[i]*(x+2)*(x+1)-e[i]*(2*x+3)+f[i];
i-=lowbit(i);
}
return ans/2;
}
int main(){
register int i,j,l;
in(n);
for(i=1;i<=n;++i){
in(a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-(b+1);
for(i=1;i<=n;++i){
t[find(a[i])].push_back(i);
}
for(i=1;i<=cnt;++i){
t[i].push_back(n+1);
l=0;
for(j=0;j<t[i].size();++j){
y=2*j-l+n+1;
x=2*j-t[i][j]+n+2;
ans+=query(y-1)-(x>=3?query(x-2):0);
add(x,1);
add(y+1,-1);
l=t[i][j];
}
l=0;
for(int j=0;j<t[i].size();++j){
y=2*j-l+n+1;
x=2*j-t[i][j]+n+2;
add(x,-1);
add(y+1,1);
l=t[i][j];
}
}
printf("%lld\n",ans);
return 0;
}