【CF1181D】Irrigation

题目大意:给定 M 个城市,每年会选出一个城市举办比赛,现给出前 N 年城市举办比赛的情况。在接下来的年份中,每年会在举办比赛次数最小的城市举办比赛,如果有很多城市举办次数均为最小值,则在编号最小的城市举办比赛。现给出 Q 个询问,每次询问第 K 年在哪个城市举办比赛。

题解:
首先,记录下每个城市前 N 年举办比赛的次数,并按照举办比赛的次数为第一关键字,城市编号为第二关键字进行从小到大排序。
发现排序后城市举办比赛的情况如下图所示
1480512-20190617142715258-655222986.png
那么若当前矩形高度小于下一个小矩形高度时,一定是当前矩形高度增加,且对于大矩形内的小矩形高度呈现周期性增加。
因此,可以采用将询问离线,并将问题划分为前 i 个高度均为 h[i] 的矩形到前 i+1 个高度均为 h[i+1] 的矩形的时间。对于每个划分出来的子问题,i 个小矩形高度呈现周期性变化,可以利用取模操作得到第几个矩形高度增加。需要注意的是,这里面的第 i 个矩形为相对下标,还需要转化成具体是第几个城市,这里可以采用权值线段树上二分的操作,求出第 k 个数对应的下标即可。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
typedef long long LL;

int n,m,Q,sz[maxn<<2],ans[maxn];
struct city{int cnt,id;}c[maxn];
struct qry{int id;LL year;}q[maxn];
void insert(int o,int l,int r,int pos){
    if(l==r){++sz[o];return;}
    int mid=l+r>>1;
    if(pos<=mid)insert(o<<1,l,mid,pos);
    else insert(o<<1|1,mid+1,r,pos);
    sz[o]=sz[o<<1]+sz[o<<1|1];
}
int kth(int o,int l,int r,int k){
    if(l==r)return l;
    int mid=l+r>>1;
    if(k<=sz[o<<1])return kth(o<<1,l,mid,k);
    else return kth(o<<1|1,mid+1,r,k-sz[o<<1]);
}

void read_and_parse(){
    scanf("%d%d%d",&n,&m,&Q);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        ++c[x].cnt;
    }
    for(int i=1;i<=m;i++)c[i].id=i;
    for(int i=1;i<=Q;i++){
        LL year;scanf("%lld",&year);
        q[i].id=i,q[i].year=year;
    }
    sort(c+1,c+m+1,[](const city &a,const city &b){
        return a.cnt==b.cnt?a.id<b.id:a.cnt<b.cnt;
    });
    sort(q+1,q+Q+1,[](const qry &a,const qry &b){
        return a.year<b.year;
    });
}
void solve(){
    LL now=n;
    for(int i=1,j=1;i<=m;i++){
        LL nxt=i==m?2e18:(LL)(c[i+1].cnt-c[i].cnt)*i+now;
        insert(1,1,m,c[i].id);
        while(j<=Q&&q[j].year<=nxt){
            LL year=q[j].year;
            int idx=(year-now)%i;
            if(idx==0)idx=i;
            ans[q[j].id]=kth(1,1,m,idx);
            ++j;
        }
        now=nxt;
    }
    for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
}
int main(){
    read_and_parse();
    solve();
    return 0;   
} 

转载于:https://www.cnblogs.com/wzj-xhjbk/p/11039510.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值