把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷3587】[POI2015] POD(哈希)

点此看题面

  • 一个长度为\(n\)的环,每个点有一个颜色。
  • 要求把这个环断成两条链,满足同种颜色的点全在一条链中。
  • 求方案数以及所有方案中两条链长度差的最小值。
  • \(n,k\le10^6\)

哈希判断

先考虑单独某种颜色,设该颜色点数为\(c\),记\(s_i\)为前\(i\)个点中这种颜色的点数。

假设我们选取了\(l,r\)两个位置断开,那么\(l+1\sim r\)中这种颜色的点数必须为\(0\)\(c\),也就是说\(s_r-s_{l}\equiv0(mod\ c)\),即\(s_l\equiv s_r(mod\ c)\)

那么我们只要对于第\(i\)位把所有颜色各自的\(s_i\%c\)哈希起来求出\(h_i\)(在一个位置上只有当前位置对应颜色的\(s_i\)会发生变化),则能够选取\(l,r\)两个位置断开当且仅当\(h_l=h_r\)

因此把\(h_i\)相同的点归为一类,方案数容易计算,最小差值双指针搞一下即可。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 1000000
using namespace std;
int n,k,a[N+5],c[N+5],lst[N+5],id[N+5];struct Hash
{
	#define ull unsigned long long
	#define CU Cn ull&
	ull x,y;I Hash() {x=y=0;}I Hash(CU a) {x=y=a;}I Hash(CU a,CU b):x(a),y(b){}
	I Hash operator + (Cn Hash& o) Cn {return Hash(x+o.x,y+o.y);} 
	I Hash operator - (Cn Hash& o) Cn {return Hash(x-o.x,y-o.y);} 
	I Hash operator * (Cn Hash& o) Cn {return Hash(x*o.x,y*o.y);}
	I bool operator < (Cn Hash& o) Cn {return x^o.x?x<o.x:y<o.y;}
	I bool operator == (Cn Hash& o) Cn {return x==o.x&&y==o.y;}
}h[N+5],pw[N+5],sd(456789001,324682339);
I bool cmp(CI x,CI y) {return h[x]==h[y]?x<y:h[x]<h[y];}
int main()
{
	RI i,j;for(scanf("%d%d",&n,&k),pw[0]=i=1;i<=k;++i) pw[i]=pw[i-1]*sd;
	for(i=1;i<=n;++i) scanf("%d",a+i),++c[a[i]],lst[a[i]]=i,id[i]=i;
	for(i=1;i<=n;++i) h[i]=h[i-1]+pw[a[i]],i==lst[a[i]]&&(h[i]=h[i]-pw[a[i]]*c[a[i]],0);//哈希
	long long ans=0;RI res=n,p,q,z;for(sort(id+1,id+n+1,cmp),i=1;i<=n;i=j+1)
	{
		for(j=i;j^n&&h[id[i]]==h[id[j+1]];++j);ans+=1LL*(j-i+1)*(j-i)/2;//哈希值相同的一段
		for(p=i+1,q=i;p<=j;++p) {W(2*(id[p]-id[q+1])>=n) ++q;//双指针
			res=min(res,abs(2*(id[p]-id[q])-n)),p^(q+1)&&(res=min(res,abs(2*(id[p]-id[q+1])-n)));}//取在分界线两侧
	}return printf("%lld %d\n",ans,res),0;
}
posted @ 2021-07-19 19:46  TheLostWeak  阅读(59)  评论(0编辑  收藏  举报