[ASDFZ]序列&计数

序列

Description
初始时你有一个长度为\(n\)的序列,序列中的第\(i\)个数为\(i\).依次进行\(m\)次操作,每次操作给定正整数\(x_i\),你需要将当前序列无限循环后截取前\(x_i\)位作为新序列.
你需要求出所有操作完成后序列中\(1\)\(n\)的个数.
Input
第一行两个正整数\(n,m\).
第二行\(m\)个正整数\(x_1\)~\(x_m\).
Output
输出一行\(n\)个整数表示答案.
Sample Input

5 3
6 8 13

Sample Output

4 3 2 2 2

HINT
\(1\leq{n,m}\leq10^5,1\leq{x_i}\leq10^{18}\)
Solution

100分

显然,若\(i<j\)\(x_i\geq{x_j}\),则操作\(i\)没有用,单调栈求出所有有用的操作,\(x_i\)递增.
\(i\)次操作后序列的前\(k\)位=第\(i-1\)次操作后序列\(\times\lfloor\frac{x_i}{x_{i-1}}\rfloor+\)\(i-1\)次操作后序列的前\(x_i\;mod\;x_{i-1}\)位.
逆序处理操作.
\(f[i]\)表示第\(i\)个序列被取到的次数.
\(s[i]\)表示\(1\)~\(i\)这个整体取到的次数.
每次二分\(\leq{k}\)的第一个\(x_i\),便可以在\(O(logn)\)的时间内得到解决.

#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 100005
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
typedef long long ll;
int n,m,t;
ll x[N],f[N],s[N],q[N];
inline int read(){
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)){
		ret=(ret<<1)+(ret<<3)+c-'0';
		c=getchar();
	}
	return ret;
}
inline ll rd(){
	ll ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)){
		ret=(ret<<1ll)+(ret<<3ll)+c-'0';
		c=getchar();
	}
	return ret;
}
inline int chk(ll k,int r){
	if(k<=q[1]) return 0;
	int l=1,mid;
	while(l<r){
		mid=(l+r+1)>>1;
		if(q[mid]<=k) l=mid;
		else r=mid-1;
	}
	return l;
}
inline void Aireen(){
	n=read();m=read();
	for(int i=1;i<=m;++i) x[i]=rd();
	q[++t]=n;
	for(int i=1;i<=m;++i){
		while(t&&x[i]<=q[t]) --t;
		q[++t]=x[i];
	}
	f[t]=1ll;
	for(int i=t,j;i;--i){
		while((j=chk(q[i],i-1))){
			f[j]+=q[i]/q[j]*f[i];
			q[i]%=q[j];
		}
		s[q[i]]+=f[i];
	}
	for(int i=n-1;i;--i)
		s[i]+=s[i+1];
	for(int i=1;i<=n;++i)	
		printf("%lld ",s[i]);
	printf("\n");
}
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
}

计数

Description
给定一个长度为\(n\)的序列\(x\),你需要求出有多少组\((a,b,c,d)\)满足\(1\leq{a}\leq{b}<c\leq{d}\leq{n}\)\(min(x_a,\dots,x_b)\leq{min(x_c,\dots,x_d)}\).
Input
第一行一个正整数\(n\).
第二行\(n\)个正整数\(x_1,\dots,x_n\).
Output
一行一个整数表示答案,对\(998244353\)取模.
Sample Input

10
8 2 4 3 5 3 1 5 8 3

Sample Output

235

HINT
\(n\leq500000,1\leq{x_i}\leq{10^9}\).
Solution

100分

\(l_i,r_i\)表示\(i\)向左/右能扩展到最远的满足区间最小值\(<x_i/\leq{x_i}\)的下标.

\(r_i,l_j\)的大小关系分类讨论.

\(r_i<l_j\)时,方案数为\((i-l_i+1)\times(r_i-i+1)\times(j-l_j+1)\times(r_j-j+1)\).

\(r_i\geq{l_j}\)时由于\(x_i<x_j\),容易证明\(l_i\leq{i}<l_j\leq{j}\leq{r_j}\leq{r_i}\),方案数为\((i-l_i+1)\times(r_j-j+1)\times[(l_j-i)\times(j-l_j+1)+C_{j-l_j+1}^2]\).

两种情况的分界点为\(r_i\),用差分的思想,树状数组维护即可.

#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 500005
#define M 998244353
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
int q[N],t;
int s1[N],s2[N],s3[N];
int x[N],p[N],l[N],r[N],n,m,ans;
inline int read(){
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)){
		ret=(ret<<1)+(ret<<3)+c-'0';
		c=getchar();
	}
	return ret;
}
inline int lowbit(int x){
	return x&(-x); 
}
inline void add1(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i))
		s1[i]=(s1[i]+k)%M;
}
inline void add2(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i))
		s2[i]=(s2[i]+k)%M;
}
inline void add3(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i))
		s3[i]=(s3[i]+k)%M;
}
inline int ask1(int x){
	int ret=0;
	for(int i=x;i;i-=lowbit(i))
		ret=(ret+s1[i])%M;
	return ret; 
}
inline int ask2(int x){
	int ret=0;
	for(int i=x;i;i-=lowbit(i))
		ret=(ret+s2[i])%M;
	return ret; 
}
inline int ask3(int x){
	int ret=0;
	for(int i=x;i;i-=lowbit(i))
		ret=(ret+s3[i])%M;
	return ret; 
}
inline bool cmp(int x,int y){
	return r[x]<r[y];
}
inline void Aireen(){
	n=read();
	for(int i=1;i<=n;++i)
		p[i]=x[i]=read();
	sort(p+1,p+1+n);
	m=unique(p+1,p+1+n)-p-1;
	for(int i=1;i<=n;++i) 
		x[i]=lower_bound(p+1,p+1+m,x[i])-p;
	for(int i=1;i<=n;++i){
		while(t&&x[i]<x[q[t]]) --t;
		if(t) l[i]=q[t]+1;
		else l[i]=1;
		q[++t]=i;
	}
	t=0;
	for(int i=n;i;--i){
		while(t&&x[i]<=x[q[t]]) --t;
		if(t) r[i]=q[t]-1;
		else r[i]=n;
		q[++t]=i;
	}
	for(int i=1;i<=n;++i) p[i]=i;
	sort(p+1,p+1+n,cmp);
	for(int i=1,j=1;i<=n;++i){
		while(j<=n&&r[p[j]]+1==i){
			add1(x[p[j]],-(p[j]-l[p[j]]+1));
			add2(x[p[j]],-1ll*(p[j]-l[p[j]]+1)*(-p[j])%M);
			add3(x[p[j]],1ll*(p[j]-l[p[j]]+1)*(r[p[j]]-p[j]+1)%M);
			++j;
		}
		ans=(ans+1ll*ask1(x[i])*(i-l[i]+1)%M*l[i]%M*(r[i]-i+1)%M)%M;
		ans=(ans+1ll*ask2(x[i])*(i-l[i]+1)%M*(r[i]-i+1)%M)%M;
		ans=(ans+1ll*ask1(x[i])*(1ll*(i-l[i]+1)*(i-l[i])/2%M)%M*(r[i]-i+1)%M)%M;
		ans=(ans+1ll*ask3(x[i])*(i-l[i]+1)%M*(r[i]-i+1)%M)%M;
		add1(x[i],i-l[i]+1);
		add2(x[i],1ll*(i-l[i]+1)*(-i)%M);
	}
	printf("%d\n",(ans+M)%M);
}
int main(){
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
}

2017-04-05 23:39:02

posted @ 2021-11-26 20:06  Aireen_Ye  阅读(112)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.