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; }