首先分成N^0.5块,然后答案显然是中间一整段的最大值,或者是两端零星的部分。
那么可以得到f[i][j]表示第i块到第j块的答案;然后就需要快速求出零星部分出现的次数,用一个前缀和s[i][j]表示在前i块中j出现的次数。
离散化搞一搞。
好像莫队也是可以的把。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define ll long long #define N 100005 #define M 335 using namespace std; int n,m,cnt,c[N],num[N],g[N],s[M][N],l[M],r[M],blg[N]; ll f[M][M]; struct node{ int x,y; }a[N]; int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x; } bool cmp(node u,node v){ return u.x<v.x; } int main(){ n=read(); int i,j,k,cas=read(); for (i=1; i<=n; i++){ a[i].x=read(); a[i].y=i; } sort(a+1,a+n+1,cmp); for (i=1; i<=n; i++){ if (i==1 || a[i].x!=a[i-1].x) num[++cnt]=a[i].x; c[a[i].y]=cnt; } m=(int)sqrt(n); for (i=1; i<=m; i++){ l[i]=r[i-1]+1; r[i]=r[i-1]+m; } r[m]=n; for (i=1; i<=m; i++) for (j=l[i]; j<=r[i]; j++) blg[j]=i; for (i=1; i<=m; i++){ memcpy(s[i],s[i-1],sizeof(s[i])); for (j=l[i]; j<=r[i]; j++) s[i][c[j]]++; } for (i=1; i<=m; i++){ for (j=i; j<=m; j++){ f[i][j]=f[i][j-1]; for (k=l[j]; k<=r[j]; k++){ g[c[k]]++; f[i][j]=max(f[i][j],(ll)g[c[k]]*num[c[k]]); } } for (j=1; j<=cnt; j++) g[j]=0; } int x,y,u,v; ll ans; while (cas--){ x=read(); y=read(); u=blg[x]; v=blg[y]; if (u+1<v){ ans=f[u+1][v-1]; for (i=x; i<=r[u]; i++) g[c[i]]++; for (i=y; i>=l[v]; i--) g[c[i]]++; for (i=x; i<=r[u]; i++) ans=max(ans,(ll)(g[c[i]]+s[v-1][c[i]]-s[u][c[i]])*num[c[i]]); for (i=y; i>=l[v]; i--) ans=max(ans,(ll)(g[c[i]]+s[v-1][c[i]]-s[u][c[i]])*num[c[i]]); for (i=x; i<=r[u]; i++) g[c[i]]=0; for (i=y; i>=l[v]; i--) g[c[i]]=0; } else{ ans=0; for (i=x; i<=y; i++){ g[c[i]]++; ans=max(ans,(ll)g[c[i]]*num[c[i]]); } for (i=x; i<=y; i++) g[c[i]]=0; } printf("%lld\n",ans); } return 0; }
by lych
2016.3.25