【AGC003 E】Sequential operations on Sequence

Description

  你有一个长度为 \(n\) 的序列,第 \(i\) 项为 \(i\)
  有 \(m\) 次操作,每次操作给定一个 \(x\),你需要将序列无限循环后截取前 \(x\) 项,作为新序列。
  问最后序列中每个数出现多少次。
  \(n,m\le 10^5,\space x_i\le 10^{18}\)

Solution

  有个很显然的性质:若存在 \(i,j\),满足 \(i\lt j\)\(x_i\ge x_j\),则第 \(i\) 次操作没用,可以不做。
  所以简化后的操作序列 \(x\) 是单调上升的。
  (这步可以不写,因为按照下述做法,如果出现上述情况那在第 \(i\) 个操作不会对答案造成任何贡献)

  我们发现正着做不可行,考虑反着做。
  我们假设最后的序列不是无限循环得来的,比如对于样例,假设最后得到的序列是 1 2 3 4 5 6 7 8 9 10 11 12 13
  那么所谓的“无限循环”其实就是把一段区间模 \(x\)
  依然以样例的操作为例,倒序做操作(忽略掉最后一个操作),倒数第二个操作的 \(x\)\(8\)
  那么显然第 \([9,13]\) 个数是由第 \([1,8]\) 个数循环得来的。
  故把第 \([9,13]\) 个数模 \(8\),得到新序列 1 2 3 4 5 6 7 8 1 2 3 4 5
  这时要把区间 \([1,8]\) 和区间 \([9,13]\) 分成两个独立区间扔进堆,因为以后 \([1,8]\) 区间再被模时,\([9,13]\) 区间作为 \([1,8]\) 区间的翻版,也可能被模。

  如果把样例的倒数第二个操作的 \(x\) 改为 \(5\) 呢?我们需要分出 \([1,5]\)\([6,10]\)\([11,13]\) 三个区间么?
  显然不用,\([1,5]\)\([6,10]\) 两个区间显然等价,所以只记一个区间 \([1,5]\),然后系数 \(\times \space 2\) 即可。

  当一个区间长度被削到 \(\le n\) 时,设该区间长度为 \(len\),那么该区间内的数就是 \(1\)\(len\) 了。其对答案的贡献是前缀 \(+\space 1\),差分即可(即在第 \(len\) 位上 \(+\space 1\),最后从第 \(n\) 位向第 \(1\) 位递推算一遍前缀和)。

  「这样做的时间复杂度对吗?」
  当然不对了,尝试用类似线段树的形态画出分割区间的过程,发现最优情况下都是一个 \(10^{18}\) 个元素的满二叉树……
  我们发现,当我们倒序扫到第 \(i\) 个操作时,我们会把很多个区间都分成长度为 \(x_i\)\(len\% x_i\) 的两个区间。
  那我们为什么不再分出来的这些长度为 \(x_i\) 的区间再合成一个记呢?这样我们就只需要把长度为 \(len\% x_i\) 的区间再扔进堆了。
  这时复杂度就有意思了,这相当于初始时在堆中放入 \(n\) 个数 \(x_{1\cdots m}\),然后把每个数不停地模一个比自己小的数。
  这对应了一个很裸的性质:对于一个数,一直模比它小的数,最多模 \(\log n\) 次变成 \(1\),因为每模一次至少 \(÷\space 2\)
  故堆中总共会出现过 \(m\log x_i\) 个元素,每次取堆顶元素的时间是 \(O(\log m)\),总时间为 \(O(m\log m\log x_i)\)

#include<bits/stdc++.h>
#define ll long long
#define N 100001
using namespace std;
inline ll read(){
	ll x=0; bool f=1; char c=getchar();
	for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
	for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
	if(f) return x;
	return 0-x;
}
int n,m,top; ll a[N],ans[N];
struct scx{
	ll x,coe;
	inline bool operator <(const scx& a)const{
		return x<a.x;
	}
}; priority_queue<scx> pq;
int main(){
	n=a[0]=read(), m=read();
	ll x;
	for(int i=1; i<=m; ++i){
		x=read();
		while(top && a[top]>=x) --top;
		a[++top]=x;
	}
	pq.push((scx){a[top],1});
	for(int i=top-1; i>=0; --i){
		ll tmp = 0; scx u = pq.top();
		while(u.x>a[i]){
			pq.pop();
			tmp += (u.x/a[i])*u.coe, u.x %= a[i];
			if(u.x) pq.push(u);
			if(pq.empty()) break;
			u = pq.top();
		}
		if(tmp) pq.push((scx){a[i],tmp});
	}
	while(!pq.empty()){
		scx u = pq.top(); pq.pop();
		ans[u.x] += u.coe;
	}
	for(int i=n-1; i>=1; --i) ans[i]+=ans[i+1];
	for(int i=1; i<=n; ++i) printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2019-10-10 09:38  大本营  阅读(240)  评论(0编辑  收藏  举报