[二分 单调队列] Atcoder AGC003 E. Sequential operations on Sequence

首先可以发现,如果 xi>xi+1 那么 xi 就是没用的,所以我们就倒过来求一个递减的序列

然后考虑第 i 次操作后的数列,肯定是由第 i1 次操作后的数列循环几次再加一个前缀得到的

而那个前缀也是由之前的某个前缀得到的,那么就二分一下,记一下每次操作会执行几次(也就是被后面的操作覆盖几次)

瞎搞一下

因为每次操作最多二分log次,所以总复杂度是 O(nlog2n)

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N=100010;

int n,q,t;
ll b[N],Q[N],a[N],c[N];

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

inline void read(ll &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

inline void work(ll x,ll y){
  int p=upper_bound(Q,Q+1+t,x)-Q-1;
  if(!p) a[1]+=y,a[x+1]-=y;
  else c[p]+=y*(x/Q[p]),work(x%Q[p],y);
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  read(n); read(q);
  if(q==0){
    for(int i=1;i<=n;i++) printf("%d\n",1); return 0;
  }
  for(int i=1;i<=q;i++) read(b[i]);
  Q[++t]=b[q];
  for(int i=q-1;i;i--){
    if(b[i]>=Q[t]) continue;
    Q[++t]=b[i];
  }
  if(n<Q[t]) Q[++t]=n; reverse(Q+1,Q+1+t);
  c[t]=1;
  for(int i=t;i>1;i--)
    c[i-1]+=c[i]*(Q[i]/Q[i-1]),work(Q[i]%Q[i-1],c[i]);
  for(int i=1;i<=n;i++) a[i]+=a[i-1];
  for(int i=1;i<=Q[1];i++) a[i]+=c[1];
  for(int i=1;i<=n;i++) printf("%lld\n",a[i]);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值