zoukankan      html  css  js  c++  java
  • 【AGC003 E】Sequential operations on Sequence

    Description

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

    Solution

      有个很显然的性质:若存在 (i,j),满足 (ilt j)(x_ige 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]),然后系数 ( imes 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_{1cdots m}),然后把每个数不停地模一个比自己小的数。
      这对应了一个很裸的性质:对于一个数,一直模比它小的数,最多模 (log n) 次变成 (1),因为每模一次至少 (÷space 2)
      故堆中总共会出现过 (mlog x_i) 个元素,每次取堆顶元素的时间是 (O(log m)),总时间为 (O(mlog mlog 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
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    商用 三色灯 显示屏 原理概述
    高速LVDS时序到底怎么看
    quartus qsys sdram IP 使用
    Avalon 总线 时序 介绍
    以太网 mac层传输 verilog 测试程序
    quartus15.1 下程程序 电脑蓝屏 解决方法
    vivado 波形保存以及arp
    quartus timequest 使用过程中的笔记
    Modelsim 仿真指令force用法
    开发笔记(一)Kintex
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/agc003e.html
Copyright © 2011-2022 走看看