dp/思维(ACMICPC EC-Finals 2015 F-Hungry Game of Ants)

题目链接
题意:一个长为n+1的杆子。上面有n个蚂蚁,第i个蚂蚁在距离左边第i个位置,重量为i。开始后,所有蚂蚁随机从向左或者向右以相同的速度开始移动,如果走到杆子的头,他会立刻掉头。如果两个蚂蚁相遇了,则重量大的那个蚂蚁会把小的吃了,他的体重会增加小的体重。并继续往前走,如果两个蚂蚁重量相同,则左边会吃掉右边。问:第k个蚂蚁有多少种幸存到最后的情况。
题解:
首先,如果第k个蚂蚁开始往右走必死无疑。只能向左走,不难想到此时k蚂蚁只有把先k-1只蚂蚁全部吃掉之后才会和他右边的蚂蚁相遇。也就是说,在和开始右边的蚂蚁相遇时,他的体重一定是 ∑ 1 k i \sum_1^ki 1ki。这里不妨把k蚂蚁的移动分为两个阶段,吃左面蚂蚁和吃右面蚂蚁。

第一阶段(吃左面蚂蚁):
枚举k蚂蚁左面第一个往左走的蚂蚁c,显然如果 ∑ c + 1 k i > ∑ 1 c i \sum_{c+1}^ki>\sum_1^ci c+1ki>1ci则是一种合法情况,这时对于蚂蚁1到蚂蚁c-1,他们可以任意选择方向。最后累计所有情况。当然最后结果要+1,因为还有一个1到k-1都往右走的情况。

第二阶段(吃右面蚂蚁):
我们把第一阶段的答案赋值给dp[k]进行dp。
我们对于dp[p],代表考虑到第p个蚂蚁,最后一个蚂蚁往左走的方案数,同样枚举在他之前第一个往左走的蚂蚁c。显然蚂蚁k在与p相遇前会吃掉前c个蚂蚁。所以体重为 ∑ 1 c i \sum_1^ci 1ci如果满足条件 ∑ 1 c i > = ∑ c + 1 p i \sum_1^ci>=\sum_{c+1}^pi 1ci>=c+1pi则为合法状态,dp[i]需要累加dp[c]。显然,这个判定关系是单调的,遂可以二分或者尺取。样例1e7,二分会tle,所以用尺取。最后答案要*2,因为最后一只蚂蚁可以往右走。

下面是ac代码:

#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>
#include <cmath>
#define ll long long
using namespace std;

const int N = 2e6+5;
const int inf= 0x3f3f3f3f;
const int mod = 1e9+7;

inline ll getn(int l, int r)
{
    if (l > r) return 0;
    return 1ll * (r - l + 1) * (r + l) / 2;
}
ll dp[N];
ll in[N];
int main()
{
    int t; cin >> t;
    int t0 = 1;
    in[0] = 1;
        for (int i = 1; i <= N-2; i++)
            in[i] = in[i-1] * 2 % mod;
    while(t--)
    {
        int n, k;
        scanf("%d%d", &n, &k);
        ll ans = 0;
        for (int i = 0; i <= n; i++) dp[i] = 0;
        for (int i = 1; i < k; i++)
        {
            ll r = getn(i+1, k);
            ll l = getn(1, i);
            if (l >= r) continue;
            ans = (ans + in[i-1] % mod) % mod;
        }
        ans++, ans %= mod;
        ll mx = getn(1, k);
        ll ansr = 1;
        int c = k;
        dp[k] = ans;
        for (int i = k+1; i <= n; i++)
        {
            while(c < i && getn(1, c) < getn(c+1, i))
                ans = ((ans - dp[c++]) % mod + mod) % mod;
            dp[i] = ans;
            ans = (ans + dp[i]) % mod;
        }
        printf("Case #%d: %lld\n",t0++,  dp[n]*2 % mod);
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值