BZOJ4241历史研究题解
很显然可以想到分块,用f[i][j]表示块i到块j的ans,然后发现答案一定是f[i][j]
或者其他在边角出现的数字
我们在记下g[i][j]从开头到块i中的数字j出现的次数
这样就每一次就统计边角数字出现的次数,然后更新答案就好了
(好像莫对也可以做。。。。)
注意每一次查询时不要memset,会T飞
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> # include<cmath> using namespace std; typedef long long LL; const int mn = 100005; int n,m,siz; int bl[mn],b[mn],c[mn],a[mn]; LL f[405][405];//块i到j的答案 int g[405][mn];//从1到块i中每个数出现的次数 int tmp[mn],st[mn],top; void pre(int x) { LL ans=0; memset(tmp,0,sizeof(tmp)); for(int i=1;i<=n;i++) g[x][i]=g[x-1][i]; int y=min(n,x*siz); for(int i=(x-1)*siz+1;i<=y;i++) g[x][a[i]]++; for(int i=(x-1)*siz+1;i<=n;i++) { tmp[a[i]]++; if(1ll*b[a[i]]*tmp[a[i]]>ans) ans=1ll*b[a[i]]*tmp[a[i]]; if(i%siz==0) f[x][i/siz]=ans; if(i==n) f[x][bl[n]]=ans; } } LL ask(int x,int y) { LL ans=0; top=0; //memset(tmp,0,sizeof(tmp)); if(bl[x]==bl[y]) { for(int i=x;i<=y;i++) { if(tmp[a[i]]==0) st[++top]=a[i]; tmp[a[i]]++; if(1ll*b[a[i]]*tmp[a[i]]>ans) ans=1ll*b[a[i]]*tmp[a[i]]; } for(int i=top;i>=1;i--) tmp[st[i]]=0; return ans; } else { if(bl[y]-1>=bl[x]+1) ans=f[bl[x]+1][bl[y]-1]; for(int i=x;i<=bl[x]*siz;i++) { if(tmp[a[i]]==0) st[++top]=a[i],tmp[a[i]]=g[bl[y]-1][a[i]]-g[bl[x]][a[i]]; tmp[a[i]]++; if(1ll*b[a[i]]*tmp[a[i]]>ans) ans=1ll*b[a[i]]*tmp[a[i]]; } for(int i=(bl[y]-1)*siz+1;i<=y;i++) { if(tmp[a[i]]==0) st[++top]=a[i],tmp[a[i]]=g[bl[y]-1][a[i]]-g[bl[x]][a[i]]; tmp[a[i]]++; if(1ll*b[a[i]]*tmp[a[i]]>ans) ans=1ll*b[a[i]]*tmp[a[i]]; } for(int i=top;i>=1;i--) tmp[st[i]]=0; return ans; } } int main() { //freopen("4241.in","r",stdin); //freopen("4241.out","w",stdout); int x,y; scanf("%d%d",&n,&m); siz=sqrt(n*1.0); for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+1+n); for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+n,a[i])-b; /*for(int i=1;i<=n;i++) printf("%d ",a[i]);离散化*/ for(int i=1;i<=n;i++) bl[i]=(i-1)/siz+1; for(int i=1;i<=bl[n];i++) pre(i); memset(tmp,0,sizeof(tmp)); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); printf("%lld\n",ask(x,y)); } return 0; }