启发式分治

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

玛卡巴卡正在玩一个游戏,在他面前有若干堆石子,他可以选择一对相邻的石子堆,并且分别在这两堆石子中取走一个石子。如果一个石子堆被取完,那么原本与之相邻的石子堆在这堆石子取完后相邻

现在有 nnn 个石子堆,玛卡巴卡想知道有多少对 l,r (l≤r)l,r\ (l\leq r)l,r (l≤r),满足将编号为 lll 到 rrr 的石子堆单独取出进行游戏时他能取完所有的石子。

输入描述:

 

第一行一个数 n (1≤n≤105)n\ (1\leq n\leq 10^5)n (1≤n≤105)。

接下来一行 nnn 个正整数 ai (1≤ai≤109)a_i\ (1\leq a_i\leq 10^9)ai​ (1≤ai​≤109),表示每堆石子的石子数。

输出描述:

一个整数,表示答案。
#include<iostream>
using namespace std;
#define int long long
const int maxn=1e5+5;
int a[maxn];int st[maxn][40];int lg[maxn];
int sum[maxn];int odd[maxn];int even[maxn];
int get(int l,int r)
{
    int t=lg[r-l+1];
    return a[st[l][t]]<a[st[r-(1<<t)+1][t]]?st[r+1-(1<<t)][t]:st[l][t];
}
int ans=0;
void solve(int l,int r)
{
    int pos=get(l,r);
    if(l==r) return ;
    if(pos-l<r-pos)
    {
        for(int i=l;i<=pos;i++)
        {
             if(sum[r]-sum[i-1]<2*a[pos]) continue;
            int num=2*a[pos]+sum[i-1];
            int ll=pos,rr=r;
            int loc;
            while(ll<=rr)
            {
                int m=(ll+rr)/2;
                if(sum[m]>=num)
                {
                    loc=m;
                    rr=m-1;
                }
                else ll=m+1;
            }
            //cout<<loc<<endl;
            if(sum[i-1]%2==1) ans+=(odd[r]-odd[loc-1]);
            else ans+=(even[r]-even[loc-1]);
        }
    }
    else
    {
        for(int i=pos;i<=r;i++)
        {
            if(sum[i]-sum[l-1]<2*a[pos]) continue;
            int num=sum[i]-2*a[pos];
            int ll=l,rr=pos-1;
            int loc=l-1;
            while(ll<=rr)
            {
                int m=(ll+rr)/2;
                if(sum[m]<=num)
                {
                    loc=m;
                    ll=m+1;
                }
                else rr=m-1;
            }
            //cout<<loc<<endl;
            if(loc==l-1) {
                if((sum[i]-sum[l-1])%2==0)
                ans++;
                continue;
            }
            if(sum[i]%2==1) 
            {
                if(l!=1)
                ans+=(odd[loc]-odd[l-2]);
                else
                ans+=(odd[loc]-odd[l-1]);
            }
            else {
                if(l==1) {
                ans++;
                ans+=(even[loc]-even[l-1]);
                }
                else ans+=(even[loc]-even[l-2]);
            }
        }
    }
    //cout<<ans<<endl;
    if(pos-1>=l)
    solve(l,pos-1);
    if(r>=pos+1)
    solve(pos+1,r);
}
signed main()
{
    int n;cin>>n;
    lg[1]=0;
    for(int i=2;i<maxn;i++) lg[i]=lg[i/2]+1;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
        st[i][0]=i;
    }
    for(int i=1;i<=n;i++)
    {
        odd[i]=odd[i-1],even[i]=even[i-1];
        if(sum[i]%2==1) odd[i]=odd[i-1]+1;
        else even[i]=even[i-1]+1;
    }
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<(j-1))<=n;i++) {
            st[i][j]=a[st[i][j-1]]<a[st[i+(1<<(j-1))][j-1]]?st[i+(1<<(j-1))][j-1]:st[i][j-1];
        }
    solve(1,n);
        cout<<ans<<endl;
}

D. Max GEQ Sum

time limit per test

1.5 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

You are given an array aa of nn integers. You are asked to find out if the inequality

max(ai,ai+1,…,aj−1,aj)≥ai+ai+1+⋯+aj−1+ajmax(ai,ai+1,…,aj−1,aj)≥ai+ai+1+⋯+aj−1+aj

holds for all pairs of indices (i,j)(i,j), where 1≤i≤j≤n1≤i≤j≤n.

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1≤t≤1051≤t≤105). Description of the test cases follows.

The first line of each test case contains a single integer nn (1≤n≤2⋅1051≤n≤2⋅105)  — the size of the array.

The next line of each test case contains nn integers a1,a2,…,ana1,a2,…,an (−109≤ai≤109−109≤ai≤109).

It is guaranteed that the sum of nn over all test cases does not exceed 2⋅1052⋅105.

Output

For each test case, on a new line output "YES" if the condition is satisfied for the given array, and "NO" otherwise. You can print each letter in any case (upper or lower).

#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const ll INF=1e18;
int a[N];
ll sx[N][20];
ll si[N][20];
int s[N][20];
int lg[N];
ll findmx(int l,int r)
{
    int t=lg[r-l+1];
    return max(sx[l][t],sx[r-(1<<t)+1][t]);
}
ll findmn(int l,int r)
{
    int t=lg[r-l+1];
    return min(si[l][t],si[r-(1<<t)+1][t]);
}
int ff(int l,int r)
{
    int t=lg[r-l+1];
    if(a[s[l][t]]<a[s[r-(1<<t)+1][t]]) return s[r-(1<<t)+1][t];
    else return s[l][t];
}
bool fz(int l,int r)
{
    if(l>=r) return true;
    int pos=ff(l,r);
    //cout<<l<<" "<<r<<" "<<pos<<endl;
    ll num1=findmx(pos,r);
    ll num2=findmn(l-1,pos-1);
    //cout<<num1<<" "<<num2<<endl;
    if(num1-num2>a[pos]) return false;
    if(fz(l,pos-1)&&fz(pos+1,r)) return true;
    return false;
}
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sx[i][0]=sx[i-1][0]+a[i];
        si[i][0]=si[i-1][0]+a[i];
        s[i][0]=i;
    }
    for(int j=1;j<20;j++)
    {
        for(int i=0;(i+(1<<j)-1)<=n;i++)
        {
            sx[i][j]=max(sx[i][j-1],sx[i+(1<<(j-1))][j-1]);
            si[i][j]=min(si[i][j-1],si[i+(1<<(j-1))][j-1]);
        }
    }
    for(int j=1;j<20;j++)
    {
        for(int i=1;(i+(1<<j)-1)<=n;i++)
        {
            if(a[s[i][j-1]]>a[s[i+(1<<(j-1))][j-1]]) s[i][j]=s[i][j-1];
            else s[i][j]=s[i+(1<<(j-1))][j-1];
        }
    }
    if(fz(1,n)) puts("YES");
    else puts("NO");
}
int main()
{
    int t;
    cin>>t;
    for(int i=2;i<N;i++) lg[i]=lg[i/2]+1;
    while(t--)
    {
        solve();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值