【THUPC2019】令人难以忘记的题目名称 / game(数论)

传送门


题解:

倒着思考,什么样的序列必胜?首先是全0序列,然后是全等序列,然后是差分后是全等序列的。。。以此类推,必胜的序列必然满足它的某一阶差分在 %p 下是全0,且最少的差分次数就是答案。

以下将差分定义为 A i ′ = A i − A i + 1 A'_i=A_i-A_{i+1} Ai=AiAi+1,这里省略了下标的取模,默认 A A A 是一个循环序列。

首先我们知道一个差分后是数列可以写成 A i ′ = ∑ j = 0 t ( t j ) ( − 1 ) j A i + j A'_i=\sum_{j=0}^t{t\choose j}(-1)^jA_{i+j} Ai=j=0t(jt)(1)jAi+j

考虑 t = p k t=p^k t=pk 的情况,用 Lucas 定理消去为0的组合数,我们发现这个时候 A i ′ = A i − A i + p k A'_i=A_i-A_{i+p^k} Ai=AiAi+pk

假设 A A A 能够最终差分为全 0 0 0,次数为 a a a,则必然存在某个 p k ≥ a p^k\geq a pka,使得差分 p k p^k pk 之后序列为全0,则 A i ′ = 0 = A i − A i + p k A'_i=0=A_i-A_{i+p^k} Ai=0=AiAi+pk,即 A i = A i + p k A_i=A_{i+p^k} Ai=Ai+pk

我们发现限制容易变成 A i = A i + gcd ⁡ ( n , p k ) A_i=A_{i+\gcd(n,p^k)} Ai=Ai+gcd(n,pk),那么将 k k k 变大我们发现最大只需要考虑 p k ∣ n , p k + 1 ∤ n p^k|n,p^{k+1}\nmid n pkn,pk+1n,再往上就没有意义了,这个时候看一下原序列是否满足条件,满足则有解,否则无解。

求答案就很简单了,我们把 n n n 变为循环节长度 p k p^k pk,考虑进行若干次 p k − 1 p^{k-1} pk1 次差分,直到循环节长度变为 p k − 1 p^{k-1} pk1,可以证明复杂度上界是等比数列求和为 O ( n , p ) O(n,p) O(n,p)


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}

}using namespace IO;

using std::cerr;
using std::cout;

cs int N=3e5+7;

int n,p;

int A[N],B[N];

bool check(int t){
	for(int re i=0;i+t<n;++i)
		if(A[i]!=A[i+t])return false;
	return true;
}

void Main(){
	n=gi(),p=gi();
	for(int re i=0;i<n;++i)
		A[i]=gi()%p;
	int t=1;
	while(n%(t*p)==0)t*=p;
	if(!check(t)){
		puts("-1");
		return ;
	}int ans=0;
	while(t>1){
		n=t;t/=p;
		while(!check(t)){ans+=t;
			for(int re i=0;i<n;++i)
				B[i]=(A[i]-A[(i+t)%n]+p)%p;
			memcpy(A,B,sizeof(int)*n);
		}
	}cout<<ans+!!A[0]<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("game.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值