BZOJ5103 : [POI2018]Róznorodno

从上到下枚举上下底边,那么涉及两行的添加和删除。

首先预处理出对于每一列,每个位置添加和删除时,是否会对往下$k$个里出现这个颜色造成影响。

然后对于每种颜色维护一个长度为$m$的bitset,表示哪些列出现过该颜色。

那么每次修改时,找到前驱和后继,对这一行答案的影响是一段区间加,差分前缀和即可。

时间复杂度$O(\frac{nm^2}{64})$。

 

#include<cstdio>
typedef unsigned int U;
const int N=3010,M=100010,BUF=72000000;
char Buf[BUF],*buf=Buf;
int n,m,K,lim,i,j,k,u,d,a[N][N],cnt[M],s[N],mx;long long ans;bool in[N][N],ou[N][N];
U f[M][N/32+5];
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}
inline void add(U*f,int x){
  int y=x>>5,pre=-1,suf=-1;
  if(f[y]){
    int o=x&31;U z=f[y];
    for(int i=o-1;~i;i--)if(z>>i&1){pre=y<<5|i;break;}
    for(int i=o+1;i<32;i++)if(z>>i&1){suf=y<<5|i;break;}
  }
  if(pre<0)for(int i=y-1;~i;i--)if(f[i]){pre=i<<5|(31-__builtin_clz(f[i]));break;}
  if(suf<0)for(int i=y+1;i<=lim;i++)if(f[i]){suf=i<<5|__builtin_ctz(f[i]);break;}
  int l=max(pre+1,x-K+1),r=min(x,suf-K);
  if(l<=r)s[l]++,s[r+1]--;
  f[y]^=1U<<(x&31);
}
inline void del(U*f,int x){
  int y=x>>5,pre=-1,suf=-1;
  f[y]^=1U<<(x&31);
  if(f[y]){
    int o=x&31;U z=f[y];
    for(int i=o-1;~i;i--)if(z>>i&1){pre=y<<5|i;break;}
    for(int i=o+1;i<32;i++)if(z>>i&1){suf=y<<5|i;break;}
  }
  if(pre<0)for(int i=y-1;~i;i--)if(f[i]){pre=i<<5|(31-__builtin_clz(f[i]));break;}
  if(suf<0)for(int i=y+1;i<=lim;i++)if(f[i]){suf=i<<5|__builtin_ctz(f[i]);break;}
  int l=max(pre+1,x-K+1),r=min(x,suf-K);
  if(l<=r)s[l]--,s[r+1]++;
}
int main(){
  fread(Buf,1,BUF,stdin);read(n),read(m),read(K);
  for(i=1;i<=n;i++)for(j=1;j<=m;j++)read(a[i][j]);
  for(j=1;j<=m;j++){
    for(d=1;d<K;d++)if(!(cnt[a[d][j]]++))in[d][j]=1;
    for(u=0;d<=n;u++,d++){
      if(u)if(!(--cnt[a[u][j]]))ou[u][j]=1;
      if(!(cnt[a[d][j]]++))in[d][j]=1;
    }
    for(i=1;i<=n;i++)cnt[a[i][j]]=0;
  }
  lim=(m+1)>>5;
  for(i=1;i<M;i++){
    f[i][0]=1;
    f[i][(m+1)>>5]|=1U<<((m+1)&31);
  }
  for(d=1;d<K;d++)for(j=1;j<=m;j++)if(in[d][j])add(f[a[d][j]],j);
  for(u=0;d<=n;u++,d++){
    if(u)for(j=1;j<=m;j++)if(ou[u][j])del(f[a[u][j]],j);
    for(j=1;j<=m;j++)if(in[d][j])add(f[a[d][j]],j);
    for(j=1,k=0;j+K-1<=m;j++){
      k+=s[j];
      if(k>mx)mx=k;
      ans+=k;
    }
  }
  return printf("%d %lld",mx,ans),0;
}

  

posted @ 2017-12-02 02:17  Claris  阅读(614)  评论(0编辑  收藏  举报