2021杭电多校补题(5)

传送门

1. Array

题意:

给出一个长度为 n n n 的数组,求其有多少个区间满足其众数个数大于区间长度的一半。

题解:

考虑分治。

对于一个区间 ( l , r ) (l,r) (l,r),其中点为 m i d mid mid ,我们求出有多少个子区间包含 m i d mid mid 且满足题目要求。

首先来看一个性质:假设 v a l val val 为区间 ( x , y ) (x,y) (x,y)内满足要求的数,那么 v a l val val 必定也在区间 ( x , m i d ) (x,mid) (x,mid) ( m i d + 1 , y ) (mid+1,y) (mid+1,y) 中满足要求。

那么我们可以从 m i d mid mid 开始,先往左扩展,找到所有可能为答案的数,再往右扩展,也找到可能为答案的数。

然后枚举我们找到的数 x x x ,求有多少个区间满足条件的数为 x x x

把等于 x x x的数设为1,反之设为-1,先枚举左端点,碰到1则sum++,反之sum- -,那么sum>0说明区间是合法的。对sum值计数,并且维护其前缀和。

再接着枚举右端点,同样碰到1则sum++,反之sum- -,考虑右端点的答案,因为sum>0才是合法的,所以所以左端点的sum值为 ≤ s u m − 1 \leq sum-1 sum1 才行,所以直接加上前缀和即可。

代码

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=2e6+5;
const int inf=0x3f3f3f3f;
ll ans=0;
int a[MAXN];
int num[MAXN];//可能的数
int pre[MAXN];//出现次数
int vis[MAXN];
void solve(int l,int r)
{
    if(l==r){
        ans++;
        return;
    }
    int mid=(l+r)>>1;
    solve(l,mid);
    solve(mid+1,r);
    for(int i=mid;i>=l;i--){
        pre[a[i]]++;
        if(pre[a[i]]>(mid-i+1)/2){
            if(!vis[a[i]]){
                num[0]++;
                num[num[0]]=a[i];
                vis[a[i]]=1;
            }
        }
    }
    for(int i=mid;i>=l;i--){
        pre[a[i]]=0;
    }
    for(int i=mid+1;i<=r;i++){
        pre[a[i]]++;
        if(pre[a[i]]>(i-mid)/2){
            if(!vis[a[i]]){
                num[0]++;
                num[num[0]]=a[i];
                vis[a[i]]=1;
            }
        }
    }
    for(int i=l;i<=r;i++){
        vis[a[i]]=0;
        pre[a[i]]=0;
    }
    for(int i=1;i<=num[0];i++)
    {
        int sum=r-l+1,maxx=r-l+1,minn=r-l+1;
        pre[sum]=1;
        for(int j=l;j<mid;j++)
        {
            if(a[j]==num[i])
            {
                sum++;
            }
            else
            {
                sum--;
            }
            pre[sum]++;
            maxx=max(maxx,sum);
            minn=min(minn,sum);
        }
        if(a[mid]==num[i])
        {
            sum++;
        }
        else sum--;
        for(int j=minn;j<=maxx;j++){
            pre[j]+=pre[j-1];
        }
        for(int j=mid+1;j<=r;j++)
        {   
            if(a[j]==num[i])
            {
                sum++;
            }
            else
            {
                sum--;
            }
            ans+=pre[min(sum-1,maxx)];
        }
        for(int j=minn;j<=maxx;j++){
            pre[j]=0;
        }
    }
    num[0]=0;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        ans=0;
        solve(1,n);
        printf("%lld\n",ans);
    }
}


posted @ 2021-08-03 22:06  TheBestQAQ  阅读(59)  评论(0)    收藏  举报