bzoj5139 [Usaco2017 Dec]Greedy Gift Takers

http://www.elijahqi.win/2018/02/06/bzoj5139/
我这种智商低下曾经还不努力的人如果现在不努力,怕是没有大学上了
【题意】
n个奶牛排成一排,顺次编号为1到n
John每次给队头发一个礼物,然后队头移动到后面去
若奶牛i是队头,则它拿到礼物后会插入到倒数第ai个奶牛的前面
若John发了+∞次礼物,则有多少个奶牛一个礼物也拿不到呢?
【数据范围】
n≤100000
想了很久不得 于是膜了leoly的代码 什么为什么别人比我快这么多? 因为没有线段树..
每次我可以去二分一下我认为这头牛他会得到礼物 那么什么情况下他不会得到礼物呢 那么就是他前面的牛 所去到的位置全部都在他前面的时候我这头牛就一定得不到 那么就 把认为能得到礼物的边界那个人缩小

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 110000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int b[N],n,sum[N];
inline bool check(int mid){
    memset(sum,0,sizeof(sum));int ans=0;
    for (int i=1;i<=mid;++i) ++sum[b[i]];
    for (int i=1;i<=n;++i){
        ans+=sum[i];if (ans>=i) return 1;
    }return 0;
}
int main(){
    freopen("bzoj5139.in","r",stdin);
    n=read();for (int i=1;i<=n;++i) b[i]=n-read();
    int l=1,r=n;
    while(l<=r){
        int mid=l+r>>1;
        if (check(mid)) r=mid-1;else l=mid+1;
    }
    printf("%d",n-l);
    return 0;
}

线段树:leolyun的做法
可以想象 我一定是由于前面的前缀陷入了循环导致后面无法get礼物 问题在于怎么寻找这个最小的环 我可以预处理 每个位置都是由哪些牛到来 那么我可以知道 如果我到这部分的总牛数>=i的时候说明我有可能是答案了 这时候我需要去贰分一下 找到最后一个是谁填满了我前面i个前缀 如果我是前面的一部分人 恰好填满到i那么说明我那个人有可能就是答案 所以我认为只需要找到第一个与之相等的地方退出即可 即到达前i个位置的数他之前的数能够满足构成循环
update进一步思考 我认为只要陷入循环 我给他重新编号之后偶可以直接认为是一个前缀在循环就是因为这个最终循环状态中我一定是整个前缀都参与了这个循环所以 因为最后问多少个牛没拿到 不妨 进行重新编号也不会影响

#include<vector>
#include<cstdio>
#include<algorithm>
#define N 110000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct node{
    int left,right,sum;
}tree[N<<1];
vector<int>a[N];int num,n,root,ans=inf;
inline void build(int &x,int l,int r){
    x=++num;if(l==r) return;int mid=l+r>>1;
    build(tree[x].left,l,mid);build(tree[x].right,mid+1,r);
}
inline void insert1(int x,int l,int r,int p){
    tree[x].sum+=1;if (l==r) return;int mid=l+r>>1;
    if (p<=mid) insert1(tree[x].left,l,mid,p);else insert1(tree[x].right,mid+1,r,p);
}
inline int qr(int x,int l,int r,int p){
    if (!x) return inf;if (l==r) return l;int mid=l+r>>1;
    if (p<=tree[tree[x].left].sum) return qr(tree[x].left,l,mid,p);
    else return qr(tree[x].right,mid+1,r,p-tree[tree[x].left].sum);
}
inline void print(int x,int l,int r){
    int mid=l+r>>1;
    if (tree[x].left) print(tree[x].left,l,mid);
    printf("%d %d %d\n",l,r,tree[x].sum);
    if (tree[x].right) print(tree[x].right,mid+1,r);
}
int main(){
//  freopen("bzoj5139.in","r",stdin);
    n=read();for (int i=1;i<=n;++i) {int x=read();a[n-x].push_back(i);}
    build(root,1,n);
//  for (int i=1;i<=n;++i) for (int j=0;j<a[i].size();++j) printf("%d ",a[i][j]);puts("");
    for (int i=1;i<=n;++i){
        for (int j=0;j<a[i].size();++j) insert1(root,1,n,a[i][j]);//printf("%d\n",qr(root,1,n,i));
        //print(root,1,n);
        if (tree[root].sum>=i) ans=min(ans,qr(root,1,n,i));if (ans==i) break;
    }printf("%d",n-ans);
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值