您的位置:首页 > 其它

UVALIVE 7505 dp

2016-11-05 20:53 288 查看

题意:

2015年EC-final的F题

这题是半年前做的,当时想了非常久,最近又拿出来回味一下。

N只蚂蚁在一个水平放置的长度为N+1的杆子上,其中第i只蚂蚁就在据左端点i的位置上,而且重量也为i,每只蚂蚁会随机等概率地选择移动方向,向左或者向右,移动过程中到达杆子的端点就会折返。蚂蚁的移动速度都是1,两只蚂蚁相遇的时候,重量大的蚂蚁会吃掉重量小的蚂蚁,如果两只蚂蚁重量相同,则左边的会吃掉右边的,获胜的蚂蚁会加上吃掉蚂蚁的重量,移动方向不变。问,第k只蚂蚁成为最后幸存的蚂蚁有多少种情况。

思路:

刚开始看这题毫无思路,但是仔细观察可以发现这种模型的一些现象。

1.每只蚂蚁的移动速度都一样,所以如果第i只蚂蚁往左边走,它一定会先和左边的i-1只蚂蚁先相遇,这i只蚂蚁竞争之后无论谁获胜,都会形成一只重量为(i+1)*i/2的蚂蚁。

2.不在端点的任何一只蚂蚁,都一定要选择先往左边走,否则不可能活下来。

通过以上两点的分析,第k只蚂蚁向左边走,找到这只蚂蚁左边的第一个也向左边走的蚂蚁i,那么左边的i只蚂蚁会先行成一个重量为(i+1)/2的蚂蚁,而蚂蚁i+1到k-1都是向右走的,所以i会变成一个重量为(k-1-i)(i+k)/2的蚂蚁,要想蚂蚁k活下来,就要使(k-1-i)*(i+k)/2 > (i+1)/2,我们可以通过二分预处理出这个位置pos。pos+1到k-1的所有蚂蚁都往右走,这样,pos以及之前的蚂蚁无论选择什么方向,都不会影响k吃掉他们。

这样蚂蚁k就会吃掉左边的所有蚂蚁,形成一个重量为k*(k+1)/2的蚂蚁。对于右边的情况,我们就需要dp来处理。

假设dp[i]表示到第i个蚂蚁,k的右边有多少种情况让k死掉,这样最后的答案就是(2^(n-k)-dp
)*2^pos。

对于dp[i],首先如果到前i-1只蚂蚁k就死了,那么第i只蚂蚁无论选左选右,都不会影响,所以dp[i] += dp[i-1] * 2。

另外,还有一种情况是第i只蚂蚁的参与导致k死,如果想要这种情况发生,就必须要利用上面所说的二分方法求出的关于i的pos,这样pos+1到i-1都往右边走,保证这样形成的蚂蚁一定会吃掉前面pos只,这一段情况只有一种,为了保证这两种情况不重复,就要保证在k到pos这一段k不能死,情况数为(2^(pos-k)-dp[pos])

综上所述,状态转移方程为dp[i] = dp[i-1]*2 + (2^(pos-k)-dp[pos])

另外要注意对于第n个蚂蚁,无论向左还是向右对于第二种情况不影响结果,所以要乘2,很关键。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

const ll MOD = 1e9 + 7;
const int MAXN = 1e6;

int pos[MAXN + 10];
ll two[MAXN + 10], t[MAXN + 10], dp[MAXN + 10];

void init() {
for (int i = 1; i <= MAXN; i++)
t[i] = (ll)i * i * 2 + i * 2;
pos[1] = 1, pos[2] = 1;
for (int i = 2; i <= MAXN; i++)
pos[i] = lower_bound(t + 1, t + 1 + i, (ll) i * i + i) - t - 1;
for (int i = 1; i <= MAXN; i++)
two[i] = two[i - 1] * 2 % MOD;
}

int main() {
//freopen("in", "r", stdin);
init();
int T, cs = 0;
scanf("%d", &T);
while (T--) {
int n, k;
scanf("%d%d", &n, &k);
if (k == 1) {
printf("Case #%d: 0\n", ++cs);
continue;
}
if (k == n) {
printf("Case #%d: %lld\n", ++cs, two[pos[k]] * 2 % MOD);
continue;
}
dp[k] = 0;
for (int i = k + 1; i < n; i++) {
dp[i] = dp[i - 1] * 2 % MOD;
if (pos[i] >= k)
dp[i] = (dp[i] + (two[pos[i] - k] - dp[pos[i]] + MOD) % MOD) % MOD;
}
dp
= dp[n - 1] * 2 % MOD;
if (pos
>= k)
dp
= (dp
+ 2 * (two[pos
- k] - dp[pos
] + MOD) % MOD) % MOD;
ll ans = (two[n - k] - dp
+ MOD) % MOD * two[pos[k]] % MOD;
printf("Case #%d: %lld\n", ++cs, ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: