链接:http://vjudge.net/contest/143339#problem/F
题意:有n个蚂蚁依次排列在长为n-1的绳子上,且蚂蚁大小与其位置相同(位置1的蚂蚁为1,位置n的蚂蚁为n),每个蚂蚁能选择一直向左走或向右走(走到头时反向),蚂蚁速度相同,当蚂蚁相遇时,体积大的吃掉体积小的并继续移动(瞬间吃掉),问使第k个蚂蚁为最后获胜的可能有多少种(总共2^n种可能).
思路:绳子:1,2,3...k...(n-2),(n-1),n
考虑1--k这段:可以找到一个i使1+2+...+i < (i+1)+...+k,则当i右边的蚂蚁都向k方向走时,无论1--i的蚂蚁怎么走,都可以使k为最后获胜.
考虑k--n这段:从k+1遍历到n,使temp=k,每次考虑一个i使1+2+...temp > (temp+1)+...+i(i此时向左走),则在i向右走时,temp应相应向右移动,每次可求出长度为i时,k蚂蚁获胜的可能总数,最后输出dp[n].
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const long long MAXN = 1000009;
const long long MOD = 1e9+7;
long long dpben[MAXN];
long long erc[MAXN];
long long dp[MAXN];
long long SUM(long long x)
{
return x*(1+x)/2;
}
void init()
{
long long ding = 0;
erc[0] = 1;
erc[1] = 2;
for(long long i = 2;i < MAXN;i++){
erc[i] = ((erc[i-1]*2)%MOD+MOD)%MOD;
}
//printf("erc------%lld\n",erc[706]);
dpben[0] = 0;
dpben[1] = 1;
dpben[2] = 2;
for(long long i = 3;i < MAXN;i++){
while(SUM(ding) < SUM(i)-SUM(ding)){
if(ding >= i){
break;
}
ding++;
}
dpben[i] = ding%MOD;
}
return ;
}
int main()
{
long long t;
init();
scanf("%lld",&t);
for(long long Case = 1;Case <= t;Case++){
long long have,win;
for(long long i = 0;i < MAXN;i++){
dp[i] = 0;
}
scanf("%lld%lld",&have,&win);
if(have == 1 && win == 1){
printf("Case #%lld: 2\n",Case);
continue;
}
dp[win] = (erc[dpben[win]-1])%MOD;
long long ding = win;
long long temp = dp[win];
for(long long i = win+1;i <= have;++i){
while(ding < i && SUM(ding) < SUM(i)-SUM(ding)){
temp = ((temp-dp[ding++])%MOD+MOD)%MOD; //因为是取摸了的,因此减出来可能为负,要做处理
//虽然每种蚂蚁都有两种走法,但是当蚂蚁向右走时可能导致值(体积和)过大使目标蚂蚁最终吃不了,因此减去吃不了的情况
}
dp[i] = temp;
temp = (temp*2)%MOD; //每个蚂蚁都有两种走法,可能数乘二
}
long long sum = (dp[have]*2)%MOD;
printf("Case #%lld: %lld\n",Case,sum%MOD);
}
return 0;
}
其中求2次方时可以用矩阵快速幂优化一下,查找可以考虑二分查找,但是ding一定在下次有个变大或变小的趋势,所以也是线性的.