【Solution】P8313 [COCI2021-2022#4] Izbori
前言
洛谷博客链接
很棒的一道题,建议评紫。
这里提供一种
Subtask 1
暴力穷举区间,枚举众数判断即可。复杂度
Subtask 2
暴力穷举区间,区间元素个数用一个桶维护起来,区间
点击查看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
int n,a[N],cop[N],tong[N];
ll ans=0;
signed main()
{
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i],cop[i]=a[i];//cop为离散化数组
sort(cop+1,cop+n+1);
int cntl=unique(cop+1,cop+n+1)-cop-1;
for(int i=1;i<=n;++i)
a[i]=lower_bound(cop+1,cop+cntl+1,a[i])-cop;//离散化
for(int l=1;l<=n;++l)
{
int cur=a[l];
for(int r=l;r<=n;++r)
{
tong[a[r]]++;//更新桶数组
if(tong[a[r]]>tong[cur])cur=a[r];//打擂台得出出现次数最多的元素
if(tong[cur]*2>r-l+1)ans++;//判断区间众数个数一半
}
for(int r=l;r<=n;++r)tong[a[r]]--;//清空tong数组
}
cout<<ans;
return 0;
}
Subtask 3
考虑到只有
点击查看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
int n,a[N],b[N],tong[N<<1];
ll ans=0;
signed main()
{
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>a[i];
if(a[i]==1)b[i]=1;
else b[i]=-1;
}
//因为前缀和会出现负数,所以要统一偏移n
int sum=0;tong[n]++;
for(int i=1;i<=n;++i)
{
sum+=b[i];//前缀和
ans+=tong[sum+n];//更新答案
tong[sum+n]++;//更新桶
}
cout<<1ll*n*(n+1)/2-ans;//合法区间-不合法区间
return 0;
}
Subtask 4
受 Subtask 3 的启发,穷举每个种类的数,分别计算贡献。
移一下项就变为
这样问题就变成了:对于每一个
点击查看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
int n,a[N],cop[N],bsum[N<<1],s[N];
ll ans=0;
inline int lowbit(int x){return x&-x;}
inline void add(int i,int x)
{
while(i<=2*n+2)bsum[i]+=x,i+=lowbit(i);
}
inline int query(int i)
{
int res=0;
while(i)res+=bsum[i],i-=lowbit(i);
return res;
}
signed main()
{
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i],cop[i]=a[i];//cop为离散化数组
sort(cop+1,cop+n+1);
int cntl=unique(cop+1,cop+n+1)-cop-1;
for(int i=1;i<=n;++i)
a[i]=lower_bound(cop+1,cop+cntl+1,a[i])-cop;//离散化
for(int i=1;i<=cntl;++i)
{
for(int j=1;j<=n;++j)
s[j]=s[j-1]+(a[j]==i);//求出前缀和
add(n+1,1);//n+1的偏移量
for(int j=1;j<=n;++j)
{
ans+=query(2*s[j]-j+n);//树状数组查询
add(2*s[j]-j+n+1,1);
}
add(n+1,-1);
for(int j=1;j<=n;++j)
add(2*s[j]-j+n+1,-1);//清空树状数组
}
cout<<ans;
return 0;
}
设
可以发现如果有
假设这个等差数列为
设
至此问题就变成了一个区间修改,求二阶前缀和的问题。这就有很多解法了,这里只提供一个树状数组的解法。
考虑到区间修改可以通过单点修改,求前缀和来解决,问题就可已转化为单点修改,求三阶前缀和。
推的过程比较繁琐,如果不理解可以自己手模两组数据来理解。
现在问题变成了单点修改,求三阶前缀和,该如何处理?
如果不会建议先看一看这道题,ABC 256 F Cumulative Cumulative Cumulative Sum,这里讲一下树状数组的思路。
先假设
考虑
考虑
考虑
用树状数组分别维护
至此,问题就完全解决了。代码如下,写了注释。
AC 代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
int n,a[N],cop[N];
ll ans=0,bs1[N<<1],bs2[N<<1],bs3[N<<1];
//bs1,bs2,bs3维护树状数组,需要进行n+1的偏移量
vector<int>t[N];
inline int lowbit(int x){return x&-x;}
inline void add(int i,ll d)
{
int x=i;
while(i<=2*n+2)
{
bs1[i]+=d;bs2[i]+=d*x;bs3[i]+=d*x*x;
i+=lowbit(i);
}
}
inline ll query(int i)
{
ll res=0;int x=i;
while(i)
{
res+=bs1[i]*(x+2)*(x+1)-bs2[i]*(2*x+3)+bs3[i];
i-=lowbit(i);
}
return res/2;
}
//树状数组
signed main()
{
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i],cop[i]=a[i];//cop为离散化数组
sort(cop+1,cop+n+1);
int cntl=unique(cop+1,cop+n+1)-cop-1;
for(int i=1;i<=n;++i)
a[i]=lower_bound(cop+1,cop+cntl+1,a[i])-cop;//离散化
for(int i=1;i<=n;++i)t[a[i]].push_back(i);//每一类数组又一个向量存起来
for(int i=1;i<=cntl;++i)//穷举数字
{
t[i].push_back(n+1);
int lst=0;
for(int j=0;j<t[i].size();++j)
{
int y=2*j-lst+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);lst=t[i][j];
}
lst=0;
for(int j=0;j<t[i].size();++j)
{
int y=2*j-lst+n+1,x=2*j-t[i][j]+n+2;
add(x,-1);add(y+1,1);lst=t[i][j];
}//清空树状数组
}
cout<<ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现