天天看點

TopCoder SRM 512 DIV1 PickAndDelete

題意:

A有一個序列T,B有一個序列S,都包含N個數。

第i輪A要在序列T中找到一個不大于S[i]的數并将其删去。

若剛好能玩N輪,則A獲勝。

求使得A獲勝的滿足要求的序列T的個數。

題解:

因為N很小,隻有200,是以可以用DP。

将S按升序排序,然後逐漸構造T。

用dp[i][j]表示有j個最大數不超過S[i]的序列個數,注意這裡其實不是dp[i][j]=pow(S[i],j),而是至少有一個數不大于S[0],至少有兩個數不大于S[1]……以此類推。

轉移方程非常簡單,每次在j個數的基礎上再加K個在S[i]+1~S[i+1]間的數,這K個數用組合數公式計算在新的J+K的數裡面的位置。注意S[i]和S[i+1]不能相同,否則有些序列會重複算很多次,是以J+K的值必須不小于i+1,否則不可能滿足上一段最後一句話。

代碼:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.*;
class PickAndDelete
{
	static final int MAXN = 210;
	static final int MOD = 1000000007;
	int []num;
	int [][]c;
	long [][]dp;
	public PickAndDelete() 
	{
		num=new int[MAXN];
		dp=new long[2][MAXN];
		c=new int[MAXN][MAXN];
		c[0][0]=1;
		c[0][1]=0;
		for(int i=1;i<MAXN;++i)
		{
			c[i][0]=1;
			for(int j=1;j<=i;++j)
				c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
		}
	}
	int pow(long a,int p)
	{
		long ans=1;
		while(p>0)
		{
			if((p&1)==1)
				ans=ans*a%MOD;
			a=a*a%MOD;
			p>>=1;
		}
		return (int)ans;
	}
	public int count(String[] S)
	{
		String str=new String();
		for(int i=0;i<S.length;++i)
			str=str+S[i];
		S=str.split(" ");
		int len=S.length;
		for(int i=0;i<len;++i)
			num[i]=Integer.parseInt(S[i]);
		Arrays.sort(num,0,len);
		int s=0;
		for(int i=0;i<=len;++i)
			dp[s][i]=0;
		dp[s][0]=1;
		int pre=0;
		for(int i=0;i<len;s^=1)
		{
			for(int j=0;j<=len;++j)
				dp[s^1][j]=0;
			int cnt=1;
			++i;
			while(i<len&&num[i]==num[i-1])
			{
				++i;
				++cnt;
			}
			for(int k=0;k<=len;++k)
				if(dp[s][k]>0)
					for(int j=0;k+j<=len;++j)
						if(k+j>=i)
							dp[s^1][k+j]=(dp[s^1][k+j]+dp[s][k]*pow(num[i-1]-pre,j)%MOD*c[k+j][k])%MOD;
//			for(int j=i;j<=len;++j)
//				dp[s^1][j]=(dp[s^1][j]+dp[s][j])%MOD;
			pre=num[i-1];	
		}
		return (int)dp[s][len];
	}
}
public class Main
{
	public static void main(String []args)throws FileNotFoundException
	{
		InputStream fin=new FileInputStream(new File("J:\\MyDocument\\Code\\input.txt"));
		Scanner scan=new Scanner(fin);
		PickAndDelete pic=new PickAndDelete();
		while(scan.hasNext())
		{
			int n=scan.nextInt();
			scan.nextLine();
			String [] s=new String[n];
			for(int i=0;i<n;++i)
				s[i]=scan.nextLine();
			System.out.println(pic.count(s));
		}
	}
}