kmjp's blog

競技プログラミング参加記です

yukicoder : No.2592 おでぶなおばけさん 2

なんでこんなタイトルなんだろ。
https://yukicoder.me/problems/no/2592

問題

N要素の整数列Aが与えられる。
また正整数Kが与えられる。

以下のクエリを処理せよ。

  • 部分列A[L,R]が指定される。整数xに対し、  \displaystyle \sum_{i=L}^R A_ix^{i-L}の値がどんな(x-K)でも割り切れるか判定せよ

解法

上記式をxの多項式f(x)とみなしたとき、f(x)が(x-K)で割り切れるとは、f(K)=0であることと等しい。
事前にA[i]*K^iのprefix sumを求めておこう。すなわち \displaystyle g(n) = \sum_{i=1}^n A_ix^{i-1}とすると、g(R)-g(L-1)=0かどうかが判定できればよい。
あいにく、g(n)はとても大きな数になるので、具体的な値を常時メモリに保持することはできない。
そこで、適当な素数でmodを取りそれで判定すればよい。

ll N,Q,K;
ll A[202020];
ll S[3][202020];
ll mo[3];

//素数判定
bool isprime(ll v) {
	for(ll i=2;i*i<=v;i++) if(v%i==0) return false;
	return (v!=1);
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>Q>>K;
	srand(time(NULL));
	FOR(i,3) {
		while(1) {
			ll a=1000000000+rand()%100000000;
			if(__gcd(a,K)>1) continue;
			if(isprime(a)==0) continue;
			mo[i]=a;
			break;
		}
	}
	ll nk[3]={1,1,1};
	FOR(i,N) {
		cin>>A[i];
		FOR(j,3) {
			S[j][i+1]=(S[j][i]+(A[i]%mo[j]*nk[j]%mo[j])+mo[j])%mo[j];
			nk[j]=nk[j]*(K%mo[j])%mo[j];
		}
	}
	while(Q--) {
		int L,R;
		cin>>L>>R;
		L--;
		int ng=0;
		FOR(i,3) if((S[i][R]-S[i][L]+mo[i])%mo[i]==0) ng++;
		if(ng==3) {
			cout<<"No"<<endl;
		}
		else {
			cout<<"Yes"<<endl;
		}
	}
	
	
}

まとめ

本番でさっとこの解法にたどり着けるかな…。