联考20200608 T1 Endless

题目:
联考20200608 T1 Endless_第1张图片
联考20200608 T1 Endless_第2张图片

分析:
原来平方串就是两个相同的串拼一起啊(大雾
考虑暴力的过程,将\(K\)种边按边权排序,暴力实现Kruskal算法
复杂度是\(O(n^2)\)
瓶颈就在于并查集
我们考虑培增,\(f[k][x]\)表示\([x,x+2^k)\)上的公共祖先,如果不在同一个并查集上,\(f[k][x]\)指向\(x\)
这个只是为了方便判断,不含实际意义
求长度为\(L\)的平方串,我们可以把原串划分成长度为\(L\)的若干串,每次求出相邻两个串的LCP和LCS
LCP与LCS的相交部分,就是可以连边的中点范围
求LCP和LCS可以直接二分哈希
联考20200608 T1 Endless_第3张图片
上图蓝色部分便是可以连边的中点范围
自己分析一下就可以发现以这些点为中点长度为\(2L\)的串是平方串
然后在倍增数组上连边,如果成功连边则往下继续连边,否则停止
由于\(k\)\(log\)级别的,每一层最多连\(O(n)\)条边,并查集平均单次复杂度\(O(\alpha(n))\)
总复杂度\(O(nlogn\alpha(n))\)
具体的trick看代码吧

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define maxn 300005
#define INF 0x3f3f3f3f
#define base 998244353
#define MOD 1000000007

using namespace std;

inline long long getint()
{
	long long num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,W;
int a[2*maxn];
int pw2[maxn],lg[maxn];
int hs[2*maxn],pw[2*maxn];
struct node{
	int w,L;
}q[maxn];
inline bool cmp(node x,node y){return x.w>1;
		if((hs[x+mid]-1ll*hs[x]*pw[mid]%MOD+MOD)%MOD==(hs[x+L+mid]-1ll*hs[x+L]*pw[mid]%MOD+MOD)%MOD)l=mid;
		else r=mid-1;
	}
	return l;
}
inline int getlcs(int x,int L)
{
	int l=0,r=L;
	while(l>1;
		if((hs[x]-1ll*hs[x-mid]*pw[mid]%MOD+MOD)%MOD==(hs[x+L]-1ll*hs[x+L-mid]*pw[mid]%MOD+MOD)%MOD)l=mid;
		else r=mid-1;
	}
	return l;
}

inline void merge(int x,int y,int k)
{
	int r1=find(k,x),r2=find(k,y);
	if(r1==r2)return;
	f[k][r1]=r2;
	if(!k){ans+=W;return;}
	merge(x,y,k-1),merge(x+pw2[k-1],y+pw2[k-1],k-1);
}

inline void work(int L,int R,int len)
{
	int k=lg[R-L+1];
	merge(L,L+len,k),merge(R-pw2[k]+1,R-pw2[k]+1+len,k);
}

inline void solve(int L)
{
	for(int i=L;i+L<=n;i+=L)
	{
		int tmp1=getlcp(i,L),tmp2=getlcs(i,L);
		if(tmp1+tmp2>=L)work(i-tmp2+1,i+tmp1,L);
	}
}

int main()
{
	int T=getint();
	pw2[0]=1;
	for(int i=1;i<=18;i++)pw2[i]=pw2[i-1]<<1,lg[pw2[i]]=i;
	for(int i=1;i

联考20200608 T1 Endless_第4张图片

你可能感兴趣的:(联考20200608 T1 Endless)