[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