1. 程式人生 > >Agc003_E Sequential operations on Sequence

Agc003_E Sequential operations on Sequence

傳送門

 

題目大意

$1,2...n,n$個數從小到大排列,有$m$此操作,每次操作給定一個引數$x$,將當且數列作為迴圈節無限地展開下去,再取前$x$個作為新的數列,求最終的數列每個數出現的次數。

$n,m\leq 10^5,x\leq 10^{18}$

 

題解

人類智慧題

首先對於兩個$x$不遞增的連續操作,上一次操作是沒有意義的,對於第一次操作$x<n$,要特判出來。

記當前數列長度為$len$,新家入的操作是$x$,先算出$x$中有多少個$len$,就知道最終答案會由多少個$len$代表的數列構成。由於每一步操作留下的數列一定會作為迴圈節的前綴出現在新數列中,我們將$x\mod len$的值計算出來,再遞迴找到最後一個操作使得該操作結束後$len<x$,遞迴處理即可,直到$x\leq n$,這樣就將$1-x$的數依次加入最終答案即可。

考慮從最後一個操作向前遞推,不斷求出形如$1-x$獨立出現了多少次,最終統計答案即可。

由於對於每一步操作,每一次查詢$len$出現的位置需要二分,每次$x$由於取模的緣故會至少變為原來的一半,所以是查詢不會超過$\log x$次,複雜度為$O(m\log m\log x+n)$。

 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 100020
using namespace std;
const int BS=1<<19;char BF[BS],OT[BS],*SZ=OT,*HD,*TL;
const char *ED=OT+BS-1; char SS[20]; int Top;
char Getchar(){if(HD==TL) TL=(HD=BF)+fread(BF,1,BS,stdin);return *HD++;}
void flush(){fwrite(OT,1,SZ-OT,stdout);}
void Putchar(char c){*SZ++ =c;if(SZ==ED) flush(),SZ=OT;}
void write(LL x){
	if(!x){Putchar(x),Putchar('\n');}
	while(x) SS[++Top]=(x%10+'0'),x/=10;
	while(Top) Putchar(SS[Top]),Top--; Putchar('\n');
}
LL read(){
	LL nm=0,fh=1; char cw=Getchar();
	for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
LL n,T,m,p[M],N,G[M],F[M],K[M],tmp;
int main(){
	n=read(),T=read();
	while(T--){LL x=read(); while(m&&p[m]>=x) m--; p[++m]=x;}
	if(m&&p[1]<n){N=n,n=p[1];for(LL i=1;i<m;i++) p[i]=p[i+1];m--;}
	G[0]=p[0]=n,F[m]=1;
	for(LL i=m;i>=0;i--){
		LL res=p[i],now=i-1;
		while(res>n){
			LL k=res/p[now]; F[now]+=F[i]*k,res-=k*p[now];
			now=upper_bound(p,p+now,res)-p-1;
		} G[i]=res,K[1]+=F[i],K[G[i]+1]-=F[i];
	}
	for(LL i=1;i<=n;i++) K[i]+=K[i-1],write(K[i]);
	while(n<N) n++,Putchar('0'),Putchar('\n'); flush(); return 0;
}