bzoj 4241: 历史研究 分块

来源:互联网 发布:数据不可信的 英文 编辑:程序博客网 时间:2024/05/12 04:40

       首先分成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 335using 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

0 0