Loading

贪心,排序,二分,分治

题目描述

马上就要放暑假啦!

为了激励一下公司的员工,徐老师决定给所有人发奖金!

发奖金的方式非常简单,徐老师会在桌上一字排开 \(n\) 张纸团,每张纸团上会写着一个数字 \(a_i\),为了平衡公司成本,这里的数字可能会存在负数

游戏规则是:每人只能抓一次,只能抓取一段连续的纸团,所有纸团上的数字和就是这次可以获得的奖金,如果有人不幸获得了负数的奖金,那么就意味着他要被扣掉对应的工资,所以徐老师也允许大家选择不抓纸团,那么奖金就是 \(0\)

现在石老师是第一个抽奖金的人,石老师想知道,他一共有多少种不同的方案可以获得至少 \(k\) 元的奖金?

输入格式

输入第一行包含一个整数 \(n,k\) 含义如题

输入第二行包含 \(n\) 个整数,\(a_i\) 依次表示每个纸团上的数字

输出格式

输出一个整数表示有多少种不同的方案

对于 \(100\%\) 的数据:\(1\le n \le 3\times 10^5,-10^{18}\le k \le 10^{18},-10^9\le a_i\le 10^9\)

点击查看


bool M1;
#include<bits/stdc++.h>
#define int long long
const int N = 2e6+10, inf = 1e18;
using namespace std;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return x*f;
}
int n,K,a[N],ans,mp[N]; int b[N],tot;
bool cmp(int x,int y) {return x>y;}
int get(int l,int r,int k) {
    int res=0;
    while(l<=r) {
        int mid=(l+r)>>1;
        if(b[mid]>=k) l=mid+1,res=mid;
        else r=mid-1;
    } return res;
}
void solve(int l,int r) {
    if(l==r) {if(a[l]>=K) ++ans;return ;}
    int mid=(l+r)>>1; solve(l,mid);solve(mid+1,r);
    tot=0;mp[mid]=a[mid];b[++tot]=mp[mid+1]=a[mid+1];
    for(int i=mid-1;i>=l;i--) mp[i]=mp[i+1]+a[i];
    for(int i=mid+2;i<=r;i++) mp[i]=mp[i-1]+a[i],b[++tot]=mp[i];
    sort(b+1,b+tot+1,cmp); for(int i=mid;i>=l;i--) ans+=get(1,tot,K-mp[i]);
} bool M2;
signed main() {
    cerr<<(&M2-&M1)/(1024*1024)<<'M'<<'B'<<endl;
    freopen("money.in","r",stdin);
    freopen("money.out","w",stdout);
    n=read();K=read(); if(K<=0) ans++;
    for(int i=1;i<=n;i++) a[i]=read();
    solve(1,n); return cout<<ans,0;
}

分治

贪心

贪心

贪心+二分

贪心+二分

二分

贪心

二分

贪心

贪心

点击查看

题目转化:

因为反转的区间的长度只能是偶数,所以每次反转完了之后就相当于反转一个位置的奇偶性。

我们把两个相邻的 \(H\)\(G\) 两两配对看成一体,定义 \(HG\)\(1\)\(GH\) 为零,出现 \(GG\) 或者 \(HH\) 就不管它。

再结合上面的转化,每次交换都可以使得一个前缀整体 \(0/1\) 翻转,最终我们的目的是使得整个序列变为全 \(1\) 的序列。


#include<bits/stdc++.h>
const int N = 2e6+10;
using namespace std;
int n,a[N],len;
signed main() {
    cin>>n; string s;cin>>s; s=' '+s;
    for(int i=1;i<=n;i+=2) {
        if(s[i]=='H'&&s[i+1]=='G') a[++len]=1;
        else if(s[i]=='G'&&s[i+1]=='H') a[++len]=0;
    } int ans=1;
    for(int i=2;i<=len;i++) {
        if(a[i]!=a[i-1]) ans++;
    } ans-=(a[len]==1);
    cout<<ans;
    return 0;
}

贪心

点击查看

这题是反悔贪心。

先看数据范围 \(A,B\) 的范围都很小,所以可以每单位的泥土分开考虑。

对于每个花坛可能多也可能少,这两种情况其实是等价的,我们只讨论少的情况:

\(val\) 为处理当前这一单位泥土的花费 \((i>j)\)

\[\begin{align*} val_i&=min(X, Z \times (i-j) - val_j)\\ &=min(X, Z \times i - Z \times j - val_j)\\ \end{align*} \]

所以每次处理完一个之后,只需要把他直接买或者移走的泥土的 \(- Z \times j - val_j\) 加到一个 \(set\) 中,直接维护就行了。

贪心

posted @ 2025-11-07 21:57  dfgz  阅读(33)  评论(3)    收藏  举报