以下是SPOJ的档案PROBLEM。示例testCase正在通过,但我在提交时获得了W / A.我错过了一些testCase(testCases)。需要帮助来弄清楚我错过了什么案例和/或我在这里做错了什么。
Ada the Ladybug正和她的朋友Velvet Mite Vinit一起玩Divisors游戏。游戏有以下规则。他们之间有一堆N石头。正在移动的玩家可以选择至少1个最多σ(N)个宝石(其中σ(N)代表N的除数)。显然,每次移动后N都会发生变化。不会得到任何结石(N == 0)的人会输。
Ada瓢虫是一位女士,所以她先行动。你能决定谁将成为赢家吗?假设两个玩家都玩得最好。
输入
第一行输入将包含1≤T≤10^ 5,测试用例数。 下一条T线将包含1≤N≤2* 10 ^ 7,这是最初堆积的石块数。
输出
输出获胜者的名字,所以“Ada”或“Vinit”。
示例输入:
8
1
3
5
6
11个
1000001个
百万
29
示例输出:
阿达
VINIT
阿达
阿达
VINIT
VINIT
阿达
阿达
代码
import java.io.*;
public class Main
{
public static int max_size = 2 * (int)Math.pow(10,7) + 1;
//public static int max_size = 25;
//public static int max_size = 2 * (int)Math.pow(10,6) + 1;
public static boolean[] dp = new boolean[max_size];
public static int[] lastPrimeDivisor = new int[max_size];
public static int[] numOfDivisors = new int[max_size];
public static void main(String[] args) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
preprocess();
int t = Integer.parseInt(br.readLine());
while(t > 0)
{
int n = Integer.parseInt(br.readLine());
if(dp[n] == true)
System.out.println("Ada");
else
System.out.println("Vinit");
t--;
}
}
public static void markLastPrimeDivisor()
{
for(int i = 0 ; i < max_size ; i++)
{
lastPrimeDivisor[i] = 1;
}
for(int i = 2 ; i < max_size ; i += 2)
{
lastPrimeDivisor[i] = 2;
}
int o = (int)Math.sqrt(max_size);
for(int i = 3 ; i < max_size; i++)
{
if(lastPrimeDivisor[i] != 1)
{
continue;
}
lastPrimeDivisor[i] = i;
if(i <= o)
{
for(int j = i * i ; j < max_size ; j += 2 * i)
{
lastPrimeDivisor[j] = i;
}
}
}
/*for(int i = 1 ; i < max_size ; i++)
System.out.println("last prime of " + i + " is " + lastPrimeDivisor[i]);*/
}
public static void countDivisors(int num)
{
int original = num;
int result = 1;
int currDivisorCount = 1;
int currDivisor = lastPrimeDivisor[num];
int nextDivisor;
while(currDivisor != 1)
{
num = num / currDivisor;
nextDivisor = lastPrimeDivisor[num];
if(nextDivisor == currDivisor)
{
currDivisorCount++;
}
else
{
result = result * (currDivisorCount + 1);
currDivisorCount = 1;
currDivisor = nextDivisor;
}
}
if(num != 1)
{
result = result * (currDivisorCount + 1);
}
//System.out.println("result for num : " + original + ", " + result);
numOfDivisors[original] = result;
}
public static void countAllDivisors()
{
markLastPrimeDivisor();
for(int i = 2 ; i < max_size ; i++)
{
countDivisors(i);
//System.out.println("num of divisors of " + i + " = " + numOfDivisors[i]);
}
}
public static void preprocess()
{
countAllDivisors();
dp[0] = dp[1] = dp[2] = true;
for(int i = 3 ; i < max_size ; i++)
{
int flag = 0;
int limit = numOfDivisors[i];
//If for any i - j, we get false,for playing optimally
//the current opponent will choose to take j stones out of the
//pile as for i - j stones, the other player is not winning.
for(int j = 1 ; j <= limit; j++)
{
if(dp[i - j] == false)
{
dp[i] = true;
flag = 1;
break;
}
}
if(flag == 0)
dp[i] = false;
}
}
}
答案 0 :(得分:1)
countDivisors()
功能中存在一个微妙的错误。它假定
lastPrimeDivisor[num]
- 如名称所示 - 返回
给定参数的最大主要因素。
但事实并非如此。例如,lastPrimeDivisor[num] = 2
对于所有偶数,或lastPrimeDivisor[7 * 89] = 7
。
原因是在
public static void markLastPrimeDivisor()
{
// ...
for(int i = 3 ; i < max_size; i++)
{
// ...
if(i <= o)
{
for(int j = i * i ; j < max_size ; j += 2 * i)
{
lastPrimeDivisor[j] = i;
}
}
}
}
仅更新从i * i
开始的数组元素。
所以lastPrimeDivisor[num]
实际上是{em> num
的一些素数除数,但不是
必然是最大的。结果,计算numOfDivisors[55447]
为8而不是正确的值6。
因此在countDivisors()
中,num
中的素数因子的指数
必须通过重复划分明确确定。
然后你可以使用除数函数乘法。这导致 以下实施:
public static void countAllDivisors() {
// Fill the `somePrimeDivisor` array:
computePrimeDivisors();
numOfDivisors[1] = 1;
for (int num = 2 ; num < max_size ; num++) {
int divisor = somePrimeDivisor[num];
if (divisor == num) {
// `num` is a prime
numOfDivisors[num] = 2;
} else {
int n = num / divisor;
int count = 1;
while (n % divisor == 0) {
count++;
n /= divisor;
}
// `divisor^count` contributes to `count + 1` in the number of divisors,
// now use multiplicative property:
numOfDivisors[num] = (count + 1) * numOfDivisors[n];
}
}
}