kmjp's blog

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

TopCoder SRM 720 Div1 Easy SumProduct

この日AIM Techと合わせてConstruction系連続で大量にレートを溶かした。
https://community.topcoder.com/stat?c=problem_statement&pm=14661

問題

桁数がblank1、blank2の2つの整数A,Bを作ることを考える。
A,Bの各桁において、0~9の各数iは合計で最大amount[i]回まで登場してよい。
また、ともにleading zeroを含んでも構わない。

そのようなA,Bの対に対し、A*Bの総和を求めよ。

解法

Aのある桁がx、Bのある桁がyであるようなケースを考えよう。
のこり(blank1+blank2-2)個の桁を、残されたamount[i]で埋める組み合わせは二項係数を使ったDPで求めることができる。
その組み合わせをf(x,y)通りとする。

さて、x,yの位置が各桁にある場合を考えると、 x*(1+10+100+...+10^{blank1-1})*y*(1+10+100+...+10^{blank2-1})*f(x,y)を答えに勘案することになる。
まとめると、 \displaystyle B_1=\sum_{i=0}^{blank1-1} 10^i, B_2=\sum_{i=0}^{blank2-1} 10^iとして \displaystyle \sum_{x=0}^9 \sum_{y=0}^9 \left( x*B_1*y*B_2*f(x,y) \right)が解となる。

class SumProduct {
	public:
	int findSum(vector <int> amount, int blank1, int blank2) {
		ll pat[2]={1,1};
		int i;
		FOR(i,blank1-1) pat[0]=(pat[0]*10+1)%mo;
		FOR(i,blank2-1) pat[1]=(pat[1]*10+1)%mo;
		
		int x,y,j,z;
		ll ret=0;
		for(x=1;x<=9;x++) if(amount[x]) {
			amount[x]--;
			for(y=1;y<=9;y++) if(amount[y]) {
				amount[y]--;
				
				ZERO(from);
				from[blank1+blank2-2]=1;
				FOR(i,10) {
					ZERO(to);
					
					for(j=0;j<=blank1+blank2-2;j++) if(from[j]) {
						FOR(z,min(j,amount[i])+1) (to[j-z]+=from[j]*combi(j,z))%=mo;
					}
					swap(from,to);
				}
				
				(ret += x*y*pat[0]%mo*pat[1]%mo*from[0])%=mo;
				amount[y]++;
			}
			amount[x]++;
		}
		return ret;
		
	}
}

まとめ

Easyにしてはちょっとしんどい。