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; }
相关文章推荐
- 【UVALive 7505】Hungry Game of Ants(DP)
- UVAlive 6560 - The Urge to Merge(状压dp)
- DP->UVALive 4764
- UVALive 6669 Hidden Tree(状压DP)
- UVALive 7061 Dire Wolf (dp)(The 2014 ACM-ICPC Asia Beijing Regional Contest D)
- UVaLive 6853 Concert Tour (DP)
- UVaLive 4256 Salesmen (简单DP)
- UVALive 6631|Jingle Balls|树形DP
- UVALive 6491 You win! 状态DP
- Islands and Bridges -- UVAlive 3267(状压DP)
- uvalive 7834 状态压缩dp
- uvalive7271(A Math Problem) 数位dp
- UVALive 6042 Bee Tower (dp)
- UVALive - 6190 Beautiful Spacing [二分+dp判定]
- Cross the Wall UVALive - 5097 (贪心+斜率dp)
- UVaLive 6697 (DP)
- UVALive - 4394 String painter DP
- UVALive 5088 Alice and Bob's Trip(树形DP)
- UVAlive 5971 Permutation Counting(数论 dp)
- UVALive 4651(DP)