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;
}