BZOJ4382 : [POI2015]Podział naszyjnika

对于每种颜色,可以发现可以切的位置被分割成了若干段独立的区域。

给每个区域一个编号,将$m$种颜色的情况当成字符串来看,如果两个切口的字符串完全匹配,那么可以在这里切两刀。

可以构造hash函数,通过差分前缀和做到$O(n)$修改。

然后对于每一种相同的hash值,将切口按位置从小到大排序,通过组合数计算方案数,再通过双指针计算最小差值。

时间复杂度$O(n\log n)$。

 

#include<cstdio>
#include<algorithm>
#define N 1000010
using namespace std;
int n,m,i,j,k,y,g[N],nxt[N],ans=N;unsigned long long p,x,s[N],sum;
inline bool cmp(int x,int y){return s[x]==s[y]?x<y:s[x]<s[y];}
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline int abs(int x){return x>0?x:-x;}
int main(){
  read(n),read(m);
  for(i=1;i<=n;i++)read(j),nxt[i]=g[j],g[j]=i;
  for(i=p=1;i<=m;p*=1500007,i++){
    for(j=g[i],x=0;j;j=nxt[j]){
      if(nxt[j])s[nxt[j]]+=x;else s[1]+=x,s[g[i]]+=x;
      s[j]-=x,x+=p;
    }
  }
  for(i=1;i<=n;i++)s[i]+=s[i-1],g[i]=i;
  std::sort(g+1,g+n+1,cmp);
  for(i=1;i<=n;i=j+1){
    for(j=i;j<n&&s[g[i]]==s[g[j+1]];j++);
    for(k=y=i;k<=j;k++){
      if(y<=k)y=k+1;
      while(y<j&&abs(n-g[y]*2+g[k]*2)>=abs(n-g[y+1]*2+g[k]*2))y++;
      if(y<=j)ans=min(ans,abs(n-g[y]*2+g[k]*2));
    }
    sum+=1LL*(j-i+1)*(j-i)/2;
  }
  return printf("%llu %d",sum,ans),0;
}

  

posted @ 2016-03-13 02:29  Claris  阅读(796)  评论(0编辑  收藏  举报