【题解 P4062 & P8313】 Yazid 的新生舞会&Izbori
[COCI2021-2022#4] Izbori
题目描述
Malnar 先生正在竞选县长,这个县一共有 \(n\) 栋房屋,每栋房屋里都住着一位居民。Malnar 先生知道,选举的赢家不一定是最好的候选人,而是在选举前举办的宴会最好的候选人。因此,在选举前几天,他将邀请第 \(l\) 至 \(r(l\le r)\) 栋房屋内居住的居民,为他们准备一顿丰盛的晚餐。
Malnar 先生知道所有居民最喜欢吃的菜。在宴会上,他会准备大多数人喜欢的一道菜。如果一个人吃到了自己最喜欢吃的菜,将会投一票给 Malnar 先生。但是如果没有吃到自己最喜欢吃的菜,他们将会把票投给 Vlado 先生。如果没有来参加晚宴的居民,他们将会忘记选举,不做出任何投票。如果一个候选人获得了投了票的人中一半以上的人的支持,他将会赢得竞选。
Malnar 先生想知道,有多少组的 \((l,r)\) 可以使他赢得竞选。
输入格式
第一行包含一个整数 \(n\),表示房屋的数量。
第二行包含 \(n\) 个整数 \(a_i\),表示第 \(i\) 栋房屋内居民最喜欢的菜。
输出格式
仅一行,输出可以使 Malnar 先生赢得竞选的 \((l,r)\) 的组数。
样例 #1
样例输入 #1
2
1 1
样例输出 #1
3
样例 #2
样例输入 #2
3
2 1 2
样例输出 #2
4
样例 #3
样例输入 #3
5
2 2 1 2 3
样例输出 #3
10
提示
【样例 2 解释】
可以使 Malnar 先生赢得竞选的 \((l,r)\) 为:\((1, 1),(2, 2),(3, 3),(1, 3)\)。
【数据规模与约定】
本题采用子任务捆绑测试。
- Subtask 1(10 pts):\(1 ≤ n ≤ 300\)。
- Subtask 2(15 pts):\(1 ≤ n ≤ 2\times10^3\)。
- Subtask 3(15 pts):\(\forall i\in\{1,2,3,\dots,n\},1 ≤ a_i ≤ 2\)。
- Subtask 4(70 pts):没有额外限制。
对于 \(100\%\) 的数据,\(1 \le l\le r ≤ n ≤ 2\times10^5,1 ≤ a_i ≤ 10^9\)。
【提示与说明】
本题分值按 COCI 原题设置,满分 \(110\)。
题目译自 COCI2021-2022 CONTEST #4 T3 Izbori。
解题思路
简明题意就是:找出满足一定条件的区间个数,每个区间需满足其中最大的数出现的次数严格大于区间长度的一半。
首先,我们可以考虑枚举每一个数,求出满足这个数在区间内出现次数大于区间长度一般的的区间个数,最后累加即可。
设 \(a_i\) 为 \(1~i\) 中当前枚举数的出现个数,很明显,需要满足 \(a_r-a_{l-1}>(r-(l-1))/2\) 。
即为 \(2*a_r-r>2*a_{l-1}-(l-1)\) 。
这就是一个偏序问题了,时间复杂度 \(O(n^2logn)\) 。
考虑优化这个问题。
观察题目中找的是绝对众数,我们可以发现,从一个左端点开始找绝对众数,那么能成为绝对众数的数最多 \(log_{r-l+1}\) 个。
很容易理解,因为若要更新绝对众数,他是成倍增长的。
那么,我们就可以 \(cdq\) 分治了。
考虑左区间与右区间合并的贡献。
我们可以找出能成为绝对众数的数,最多 \(log_{r-l+1}\) 个,我们可以处理出这些数,然后按上面的方法求解偏序,加上单个数的贡献即可。
时间复杂度 \(O(nlog^2n)\) 。
Code
#include<bits/stdc++.h>
using namespace std;
int n,a[500005],d[500005],t[500005],w;
long long s=0;
bool cmp(int q,int w)
{
return q>w;
}
void gaia(int l,int r)
{
if(l==r)return;
int mid=(l+r)>>1;
gaia(l,mid);
gaia(mid+1,r);
int x=0;
w=0;
for(int i=mid;i>=l;i--)
{
d[a[i]]++;
if(d[a[i]]>(mid-i+1)/2)t[++w]=a[i];
}
for(int i=l;i<=mid;i++)d[a[i]]=0;
for(int i=mid+1;i<=r;i++)
{
d[a[i]]++;
if(d[a[i]]>(i-mid)/2)t[++w]=a[i];
}
for(int i=mid+1;i<=r;i++)d[a[i]]=0;
sort(t+1,t+w+1);
for(int i=1;i<=w;i++)
{
if(t[i]==t[i-1])continue;
if(a[mid]==t[i])d[mid]=2+mid;
else d[mid]=mid;
for(int j=mid-1;j>=l;j--)
{
d[j]=d[j+1]-1;
if(a[j]==t[i])d[j]+=2;
}
sort(d+l,d+mid+1,cmp);
if(a[mid+1]==t[i])d[mid+1]=mid;
else d[mid+1]=mid+2;
for(int j=mid+2;j<=r;j++)
{
d[j]=d[j-1]+1;
if(a[j]==t[i])d[j]-=2;
}
sort(d+mid+1,d+r+1,cmp);
x=l-1;
for(int j=mid+1;j<=r;j++)
{
while(x<mid&&d[x+1]>d[j])x++;
s+=x-l+1;
}
for(int j=l;j<=r;j++)d[j]=0;
}
return;
}
int m;
set<int> l;
map<int,int> p;
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
l.insert(a[i]);
}
int g=0;
set<int>::iterator q=l.begin();
for(;q!=l.end();q++)
{
p[*q]=++g;
}
for(int i=1;i<=n;i++)a[i]=p[a[i]];
gaia(1,n);
cout<<s+n;
return 0;
}