Codeforces Round #281 (Div. 2) E. Vasya and Polynomial

問題

多項式P(x)のうち、係数がすべて非負整数のものについて考える。すなわち、

P(x) =a_0 + a_1 * x + a_2 * x^2 + ... + a_n * x^n

としたとき、a_i>=0(0<=i<=n)となる。
3つの数t,a,bが与えられるので、P(t)=a, P(a)=bとなるような多項式P(x)の数を求めよ。

制約
1<=t,a,b<=10^18


解法

まず論外な場合(解が明らかに0になる場合)について考えます。
多項式が定数関数以外の場合、係数が全て正なのでP(x)>=xとなるはずです。
よって、

(a!=b and (t>a or a>b))

のときは答えが0です。

逆にa==bのときは、
定数関数P(x)=a
恒等関数(?)P(x)=x
の2通りが考えられ、答えはt==aのとき2、t!=aのとき1となります。
・・・と思っていると罠があって、t=a=b=1のときは
P(x)=x^n
が任意のnについて答えになるので、この時だけ答えが無限大になります。

同様の(t=1のときの)特殊ケースとして、
t=1
b=a^n(n>0)
というケースが挙げられます。この場合は
P(x)=a*x^(n-1)
が解となります。


それ以外の場合については、t,a,bの関係から多項式を明示的に求めることが出来ます。
http://jeremykun.com/2014/11/18/learning-a-single-variable-polynomial-or-the-power-of-adaptive-queries/
(Codeforcesのeditorialのコメント欄から拝借)

かいつまんで説明すると、
多項式P(x) =a_0 + a_1 * x + a_2 * x^2 + ... + a_n * x^n
に正の値を代入したとき、P(x)>=a_i(0<=i<=n)が成り立ちます。
ゆえにP(x)+1=Nとすると、

P(N)=a_0 + a_1 * N + a_2 * N^2 + ... + a_n * N^n

となり、これをNで割った余りがa_0となり、P(N)/NをNで割った余りがa_1となり、・・・
というように全ての係数を求めることができます。
(Nが十分大きくないとき、例えばN=1のときにはこの方法を使うことはできません、念のため)

というわけで、a>a_iが成り立つときは、
P(a)=bから多項式P(x)を導出し、P(t)=aが成り立つかを見てあげればよい、ということになります。
逆に言えばa>a_iと言い切れないケースがa==bの時(P(x)が定数関数の時)やt=1の時(P(x)=a*x^(n-1)やP(x)=x^nとなるとき)だったということになります。
あとはbを繰り返しaで割るという処理になりますので、a=1のときも特殊ケースとして対応します。


プログラム

#include<iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include<string>
#include<vector>
#include<cmath>
#include<stack>
#include<queue>
#include<sstream>
#include<algorithm>
#include<map>
#include<complex>
#include<ctime>
#include<set>
#include<bitset>
using namespace std;


#define li			long long int
#define rep(i,to)	for(li i=0;i<((li)(to));i++)
#define repp(i,start,to)	for(li i=(li)(start);i<((li)(to));i++)
#define pb			push_back
#define sz(v)		((li)(v).size())
#define bgn(v)		((v).begin())
#define eend(v)		((v).end())
#define allof(v)	(v).begin(), (v).end()
#define dodp(v,n)		memset(v,(li)n,sizeof(v))
#define bit(n)		(1ll<<(li)(n))
#define mp(a,b)		make_pair(a,b)
#define rin	rep(i,n)
#define EPS 1e-10
#define ETOL 1e-8
#define MOD 100000000
typedef pair<li, li> PI;

#define INF bit(60)

#define DBGP 1


#define idp if(DBGP)
#define F first
#define S second
#define p2(a,b)		idp cout<<a<<"\t"<<b<<endl
#define p3(a,b,c)		idp cout<<a<<"\t"<<b<<"\t"<<c<<endl
#define p4(a,b,c,d)		idp cout<<a<<"\t"<<b<<"\t"<<c<<"\t"<<d<<endl
#define p5(a,b,c,d,e)		idp cout<<a<<"\t"<<b<<"\t"<<c<<"\t"<<d<<"\t"<<e<<endl
#define p6(a,b,c,d,e,f)		idp cout<<a<<"\t"<<b<<"\t"<<c<<"\t"<<d<<"\t"<<e<<"\t"<<f<<endl
#define p7(a,b,c,d,e,f,g)		idp cout<<a<<"\t"<<b<<"\t"<<c<<"\t"<<d<<"\t"<<e<<"\t"<<f<<"\t"<<g<<endl
#define p8(a,b,c,d,e,f,g,h)		idp cout<<a<<"\t"<<b<<"\t"<<c<<"\t"<<d<<"\t"<<e<<"\t"<<f<<"\t"<<g<<"\t"<<h<<endl
#define p9(a,b,c,d,e,f,g,h,i)		idp cout<<a<<"\t"<<b<<"\t"<<c<<"\t"<<d<<"\t"<<e<<"\t"<<f<<"\t"<<g<<"\t"<<h<<"\t"<<i<<endl
#define p10(a,b,c,d,e,f,g,h,i,j)		idp cout<<a<<"\t"<<b<<"\t"<<c<<"\t"<<d<<"\t"<<e<<"\t"<<f<<"\t"<<g<<"\t"<<h<<"\t"<<i<<"\t"<<j<<endl
#define foreach(it,v)	for(__typeof((v).begin()) it=(v).begin(); it!=(v).end(); ++it)
#define p2p(x)		idp p2((x).F, (x).S)
#define dump(x,n)	idp{rep(i,n){cout<<x[i]<<" ";}puts("");}
#define dump2(x,n)	idp{rep(i,n){cout<<"["<<x[i].F<<" , "<<x[i].S<<"] ";}puts("");}
#define dumpi(x)	idp{foreach(it, x){cout<<(*it)<<" ";}puts("");}
#define dumpi2(x)	idp{foreach(it, x){cout<<"["<<(it)->F<<" , "<<(it)->S<<"] ";}puts("");}

#define read2d(a,w,h)	rep(i,h)rep(j,w)cin>>a[i][j]
#define dump2d(a,w,h)	rep(i,h){rep(j,w)cout<<a[i][j]<<" ";puts("");}

typedef pair<li, li> PI;


bool chk(li t, li a, li b){
	vector<li> co;
	while(b>0){
		co.pb(b%a);
		b/=a;
	}
	li ta=0;
	rep(i,sz(co)){
		ta+=co[sz(co)-1-i];
		li taa=ta*t;
		if(i==sz(co)-1)return ta==a;
		if(taa/t!=ta)return false;
		ta=taa;
	}
	return ta/t==a;
}

int main(){
	li t,a,b;
	cin>>t>>a>>b;
	li res=0;
	if(t>1){
		if(t>a && a!=b){
			puts("0");
			return 0;
		}
		if(a==b){
			res++;
			if(t==a)res++;
		}
		else{
			if(a<b)res+=chk(t,a,b);
		}
		cout<<res<<endl;
	}
	else{
		if(max(a,b)==1)puts("inf");
		else if(a==1)puts("0");
		else{
			li bb=b;
			while(bb%a==0)bb/=a;
			if(bb==1 && b>1)puts("1");
			else cout<<chk(t,a,b)<<endl;
		}
	}
	


	return 0;
}


雑感
editorialを読んで時間をかけて考えればたしかに分かりますが、
そこそこ複雑で漏れを生みやすい場合分けゲーなので、ほとんどの場合でabからP(x)が計算できることに本番中に気付いたとしてもAC取れた気は全くしないですね・・・