Agc003_E Sequential operations on Sequence

传送门

题目大意

$1,2...n,n$个数从小到大排列,有$m$此操作,每次操作给定一个参数$x$,将当且数列作为循环节无限地展开下去,再取前$x$个作为新的数列,求最终的数列每个数出现的次数。

$n,mleq 10^5,xleq 10^{18}$

题解

人类智慧题

首先对于两个$x$不递增的连续操作,上一次操作是没有意义的,对于第一次操作$x<n$,要特判出来。

记当前数列长度为$len$,新家入的操作是$x$,先算出$x$中有多少个$len$,就知道最终答案会由多少个$len$代表的数列构成。由于每一步操作留下的数列一定会作为循环节的前缀出现在新数列中,我们将$xmod len$的值计算出来,再递归找到最后一个操作使得该操作结束后$len<x$,递归处理即可,直到$xleq n$,这样就将$1-x$的数依次加入最终答案即可。

考虑从最后一个操作向前递推,不断求出形如$1-x$独立出现了多少次,最终统计答案即可。

由于对于每一步操作,每一次查找$len$出现的位置需要二分,每次$x$由于取模的缘故会至少变为原来的一半,所以是查找不会超过$log x$次,复杂度为$O(mlog mlog 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('
');}
	while(x) SS[++Top]=(x%10+'0'),x/=10;
	while(Top) Putchar(SS[Top]),Top--; Putchar('
');
}
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('
'); flush(); return 0;
}
原文地址:https://www.cnblogs.com/OYJason/p/9804622.html