kmjp's blog

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

TopCoder SRM 855 : Div1 Medium NumReverseHard

考え方は合っていたけど、バグを大量に埋め込んで詰めるのに苦戦した。
https://community.topcoder.com/stat?c=problem_statement&pm=18240

問題

整数A,Bが与えられる。
AからBの各整数xについて、xとxの桁の並び順を反転したもののうち大きい方の総和を求めよ。

解法

f(v) := 1~vの各整数について、xとxの桁の並び順を反転したもののうち大きい方の総和
とすると、f(B)-f(A-1)が解。あとはf(v)の求め方を考えよう。

まず1~vを単純に総和を取り、その後反転による寄与分を計上しよう。
先頭と末尾(x-1)桁が一致し、先頭x桁目の値Lより末尾x桁目の値Rが大きくなる場合を総当たりする。
またその間にy桁整数が入るとする。

全体の桁数(2x+y)がvより桁が小さい場合を考える。
先頭(x-1)桁は、leading zeroを持たない9*10^(x-2)通りが考えられる。
よって、LとRを反転させることで(R-L)**1*9*10^(x-2)*10^yだけ解が増加する。

2x+yがvと桁数が同じ場合、先頭(x-1)桁がvを超えない範囲になるので注意。
また、先頭x桁がLと一致する場合、真ん中のyを定めてく間で全体がvを超えないようにする必要がある。
これは桁DPで数え上げて行けばよい。

const ll mo=1000000007;
ll p10[22];
ll p10m[22];

class NumReverseHard {
	public:
	ll rev(ll a) {
		ll v=0;
		while(a) v=v*10+a%10,a/=10;
		return v;
	}
	
	ll hoge(ll v) {
		//単純な和
		if(v<=0) return 0;
		ll ret=v%mo*((v+1)%mo)%mo*(mo+1)/2%mo;
		int len=to_string(v).size();
		
		int pre,mid,L,R,i,j;
		for(pre=1;pre*2<=len;pre++) for(mid=0;pre*2+mid<=len;mid++) {
			for(L=0;L<=9;L++) {
				if(pre==1&&L==0) continue;
				
				ll mnum=p10m[mid];
				ll num;
				if(pre*2+mid<len) {
					num=(pre==1)?1:(9*p10m[pre-2]);
				}
				else {
					ll pref=v/p10[mid+pre];
					num=(v-p10[len-1])/p10[mid+pre+1]+1;
					if(L>=pref%10) num--;
				}
				num%=mo;
				
				for(R=L+1;R<=9;R++) {
					ll add=(L-R)*p10m[pre-1]+(R-L)*p10m[pre+mid];
					add=(add%mo+mo)%mo;
					(ret+=add*mnum%mo*num%mo)%=mo;
				}
				
				if(pre*2+mid==len&&L==v/p10[mid+pre]%10) {
					pair<ll,ll> from[2]={};
					from[0].first=1;
					from[0].second=L*(p10m[pre-1]-p10m[pre+mid]+mo)%mo;
					for(i=pre+mid-1;i>=pre;i--) {
						pair<ll,ll> to[2]={};
						int d=v/p10[i]%10;
						
						ll m=(p10m[len-1-i]-p10m[i]+mo)%mo;
						
						FOR(j,10) {
							(to[1].first+=from[1].first)%=mo;
							(to[1].second+=from[1].second+from[1].first*j%mo*m)%=mo;
							if(j<d) {
								(to[1].first+=from[0].first)%=mo;
								(to[1].second+=from[0].second+from[0].first*j%mo*m)%=mo;
							}
							else if(j==d) {
								(to[0].first+=from[0].first)%=mo;
								(to[0].second+=from[0].second+from[0].first*j%mo*m)%=mo;
							}
						}
						swap(from,to);
					}
					
					for(int R=L+1;R<=9;R++) {
						ret+=from[1].second;
						ret+=from[1].first*R%mo*(p10m[pre+mid]-p10m[pre-1]+mo)%mo;
						if(R*p10[pre-1]+rev(v)%p10[pre-1]<=v%p10[pre]) {
							ret+=from[0].second;
							ret+=from[0].first*R%mo*(p10m[pre+mid]-p10m[pre-1]+mo)%mo;
						}
					}
					
				}
				
				
			}
			ret%=mo;
		}
		
		
		
		
		cout<<v<<" "<<ret<<endl;
		return ret;
	}
	
	
	int getsum(long long A, long long B) {
		int i;
		p10[0]=1;
		p10m[0]=1;
		FOR(i,19) {
			p10[i+1]=p10[i]*10;
			p10m[i+1]=p10[i+1]%mo;
		}
		return (hoge(B)-hoge(A-1)+mo)%mo;
	}
}

まとめ

考え方があっていただけにミスがもったいないな。

*1:10^(x+y)-10^(x-1