【题解】「THUPC 2019」令人难以忘记的题目名称

题目链接

一个人选串,另一个人旋转。

容易发现,如果原串\(S\)有长度为\(len\)的循环节,若Alice选的串\(T\)的循环节长度也为\(len\),则不管Bob怎么转,\(S'\)的循环节长度最大为\(len\)。如果\(len=1\),那么就可以在一步之内完成。

然后就可以开始乱搞找规律了。

\(len=4,P=2\),发现可以;取\(len=2,P=3\),发现不行;取\(len=6,P=2\),发现也不行。

由此得(cāi)出结论:当\(len=P^k\)时可以胜利,反之不行。

然后来观(xiā)察(gǎo)一下\(|S|=len=P=5\)的情况,发现当\(S=[0,1,2,3,4]\)的时候,取\(T=[4,3,2,1,0]\),就可以把\(len\)变为\(1\)。继续尝试,会发现\(S=[0,2,4,1,3]\)的时候,给它加个\([3,1,4,2,0]\)也行

观察这两个序列有什么共同点,可以发现它们的差分序列\(d\;(d_i=S_i-S_{i-1},d_1=S_1-S_{len})\)的循环节长度为\(1\)

推广一下,如果\(S\)\(t\)阶差分序列的循环节长度为\(1\),那么下一步就可以使\(S\)\(t\)阶差分序列的每一位都为\(0\),即\(t-1\)阶差分序列的循环节长度为\(1\)

于是,问题就转化为了求最小的\(t\),使\(S\)\(t\)阶差分序列的循环节长度为\(1\)

但是,这还是没办法直接做。可以观察\(S\)\(t\)阶差分序列的第\(i\)\(d_{t,i}\)为多少。容易得到:

\[\large d_{t,i}=\sum_{j=0}^t(-1)^jC_t^jS_{i-j} \]

其中,下标是在模\(len\)意义下的。

这就证明了为什么\(len=P^k\)时可以胜利,因为取\(t=len\),则\(d_{t,i}=(1+(-1)^t)S_i=0\)

所以,可以考虑将\(t\)的初始值设为\(0\),每次给\(t\)加上\(P^k\),得到差分序列,然后检查循环节长度是否为\(1\)。如果为\(1\),就还原到这次操作之前的序列,并减小\(k\)

时间复杂度\(O(nP\log_Pn)\)

code:

#include
#include
int a[300002],n,p,m,k,b[300002],c[300002],ans=2,pow[300002];
int id(int val){return val<=0?val+m:val;}
inline int mod(int val){return val>=p?val-p:val;}
bool isloop(int len){
	if(n%len)return 0;
	for(int i=len+1;i<=n;i++)if(a[i]!=a[i-len])return 0;
	return 1;
}
bool iseq(){
	for(int i=2;i<=m;i++)if(a[i]!=a[i-1])return 0;
	return 1;
}
int main(){
	scanf("%d%d",&n,&p);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]%=p;
	for(m=1,k=0;m<=n&&!isloop(m);m=m*p,k++);
	if(m>n)return puts("-1")&0;pow[0]=1;
	for(int i=1;i<=k;i++)pow[i]=pow[i-1]*p;
	if(iseq()){
		if(a[1]==0)return puts("0")&0;
		else return puts("1")&0;
	}
	for(int g=k-1;g>=0;g--){
		for(int i=0;i

你可能感兴趣的:(【题解】「THUPC 2019」令人难以忘记的题目名称)