P8313 Izbori 题解
Demeanor_Roy · · 题解
原题链接
提供一种考场上想出来的
首先由于是序列计数问题,自然要往单调性方面去想。可仔细思考就会发现,众数这东西的性质好像不那么美妙,区间扩展时并不具有很好的性质,不能考虑枚举左端点然后向右扩展。
既然区间扩展不行,就考虑区间拼接。
我们先考虑两个区间拼起来是合法的,需要满足什么条件?我们用
可现在还有一个问题,上面偏序的前提是
可让我们一看题目,你就会发现题目还有一个很强的条件:获得一半以上的人支持!
我们定义一个序列有绝对众数,当且仅当一个序列的众数个数大于区间长度除以二。这时我们可以将上面的结论改一改:对于两个确定的区间拼接,
考虑序列分治,每次递归处理子区间的问题,现在我们只考虑跨过
那这又有什么用呢?不难发现,一端端点固定的一组序列,不同的绝对众数的个数是
那这时思路就明确了,先序列分治,然后对于每层来说,先求出可能的绝对众数作为
并且这样做不会算重,因为拼接后的一个序列一定只会被一个绝对众数判断为合法。
下附代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
const int N=2e5+10,delta=2e5;
int n,val[N],cnt[N<<1];
bool vis[N];
vector<int> vec,chosen;
inline LL solve(int l,int r)
{
if(l==r) return 1;
LL ans=solve(l,mid)+solve(mid+1,r);
for(int i=mid;i>=l;i--)
{
cnt[val[i]]++;
if(!vis[val[i]]&&cnt[val[i]]>(mid-i+1)/2)
{
chosen.push_back(val[i]);
vis[val[i]]=true;
}
}
for(int i=mid;i>=l;i--) cnt[val[i]]--;
for(int i=mid+1;i<=r;i++)
{
cnt[val[i]]++;
if(!vis[val[i]]&&cnt[val[i]]>(i-mid)/2)
{
chosen.push_back(val[i]);
vis[val[i]]=true;
}
}
for(int i=mid+1;i<=r;i++) cnt[val[i]]--;
for(auto x:chosen)
{
int now,minn=delta-max(mid-l+1,r-mid),maxn=delta+max(mid-l+1,r-mid);
now=0;
for(int i=mid;i>=l;i--)
{
now+=(val[i]==x);
cnt[(now<<1)-(mid-i+1)+delta]++;
}
for(int i=minn;i<=maxn;i++) cnt[i]+=cnt[i-1];
now=0;
for(int i=mid+1;i<=r;i++)
{
now+=(val[i]==x);
ans+=(mid-l+1)-cnt[(i-mid)-(now<<1)+delta];
}
for(int i=minn;i<=maxn;i++) cnt[i]=0;
}
chosen.clear();
for(int i=l;i<=r;i++) vis[val[i]]=false;
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<=n;i++) vec.push_back(val[i]);
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
for(int i=1;i<=n;i++) val[i]=lower_bound(vec.begin(),vec.end(),val[i])-vec.begin()+1;
printf("%lld",solve(1,n));
return 0;
}
完结撒花~