9.25题解

T1

正常模拟即可,下面 code 的判断保证时间晚的答案不会被更新。

#include <iostream>
using namespace std;
int n, ans, maxn = -1, r[100050];
int main()
{
    cin >> n;
    for(int i = 0, a, b;i < n;++i)
    {
        cin >> a >> b, r[a] += b;
        if(r[a] > maxn)
            maxn = r[a], ans = a;
    }
    cout << ans;
    return 0;
}

T2

$10^{18}$,带模拟妥妥 T 飞。

我们知道,一次课表循环结束后,如果当前体力足够再进行一次这个循环,那么下一次循环将会重复这次循环。

所以我们就得到了上课方案:

  1. 扫一遍课表,找到可行的方案。
  2. 一直重复这个方案,统计答案,直到体力不够为止。
  3. 返回 1,找新的可行方案。
  4. 如果 1 中没有可上的课,跳出循环。

我们就得到了 code:

#include <iostream>
#define int long long
using namespace std;
int n, t, a[1050], sum, cnt, ans;
signed main()
{
    cin >> n >> t;
    for(int i = 0;i < n;++i)
        cin >> a[i];
    while(1)
    {
        sum = cnt = 0;
        for(int i = 0;i < n;++i)
            if(t >= sum + a[i]) sum += a[i], ++cnt;
        if(!cnt) break;
        while(t >= sum) t -= sum, ans += cnt;
    }
    cout << ans;
    return 0;
}

然而还是 T 飞的。

我们知道,减法统计个数,等价于除法。

#include <iostream>
#define int long long
using namespace std;
int n, t, a[1050], sum, cnt, ans;
signed main()
{
    cin >> n >> t;
    for(int i = 0;i < n;++i)
        cin >> a[i];
    while(1)
    {
        sum = cnt = 0;
        for(int i = 0;i < n;++i)
            if(t >= sum + a[i]) sum += a[i], ++cnt;
        if(!cnt) break;
        ans += t / sum * cnt, t %= sum;
    }
    cout << ans;
    return 0;
}

T3

下面依旧分割线划分结论。


其实刀里面有很多都是没有意义的,要先去掉。


结论1:在硬度最大的刀中,耐久最大的刀一定有意义

废话。


结论2:在硬度相同的刀中,只有耐久最大的刀有意义

如果两把刀硬度一样,耐久一大一小,

那么耐久小的刀能切的,耐久大的也能切,切的还比耐久小的多,

所以耐久小的就没必要用了。


结论3:若 a 硬度 < b 硬度,则当且仅当 a 耐久 > b 耐久时,a 有意义

如果一把刀硬度比别的小,那么如果耐久也比别的小,

它能切的别的刀也能切,而且别的刀切得还多,

所以只有当它耐久比别的刀大的时候,它才有意义。


我们现在要去掉没有意义的刀。

显而易见,硬度大的刀的优先级肯定较高,

所以我们先按硬度第一关键字,耐久第二关键字排序。

接下来,既然硬度越来越低,根据结论3,耐久就要越来越高。

根据结论一,排序后的第一把刀肯定要选。

也就是找一个包含第一把刀的耐久上升子序列即可

结论2是满足的,因为:

假设原来刀序列为 p,$p[i,j]$ 区间内的刀硬度一样,耐久降序。

  1. 如果 p[i] 没有选入上升序列,那么后面的刀耐久 < p[i],也不可能选入上升序列。
  2. 如果 p[i] 选入上升序列,那么后面的刀耐久 < p[i],不可能跟在 p[i] 的后面。

所以,当硬度相同时,这个方案只会选择耐久最大的刀,满足结论2。


我们现在要给每个瓜找刀。

我们先考虑简单的给每个瓜 lower_bound

这个方法的缺陷在于,中间是不能换刀的,一把刀爆炸之前不能用另一把

但我们可以假设换刀之前用的也是新刀,旧刀就没有用过。

那么每次找的刀就不应该找当前的瓜,而是找当前刀切过最硬的瓜。

这里就会有一个问题:如果新刀的耐久 < 旧刀切过的瓜,不可能把旧刀的瓜算到新刀上,怎么处理?

这种情况就可以不管,直接按照耐久正常炸掉处理(反正都是炸)。

#include <iostream>
#include <utility>
#include <algorithm>
#include <functional>
using namespace std;
pair<int, int> p[300050], q[300050];int n, m, a[300050], cnt = 1, ans = 1, n_max = -1, n_nj;
bool cmp(pair<int, int> a, int b) {return a.first < b;}
int main()
{
    cin >> n >> m;
    for(int i = 0;i < n;++i)
        cin >> a[i];
    for(int i = 0;i < m;++i)
        cin >> p[i].first >> p[i].second;
    sort(p, p + m, greater<pair<int, int> >());
    for(int i = 0;i < m;++i)
    {
        if(!i) q[i] = p[i];
        else if(q[cnt - 1].second < p[i].second) //找上升序列
            q[cnt++] = p[i];
    }
    reverse(q, q + cnt);
    for(int i = 0;i < n;++i)
    {
        n_max = max(n_max, a[i]);++n_nj;
        pair<int, int> *t = lower_bound(q, q + cnt, n_max, cmp);
        if(t -> second < n_nj) //同时处理新刀不符合题意 & 耐久炸掉
            ++ans, n_nj = 1, n_max = a[i];
    }
    cout << ans;
    return 0;
}

T4

设 $dp[i][j][0/1]$ 表示 $i$ 星球属于 $[i-j+1,i]$ 这个势力(0/1 分别表示一个势力)。

那我们讨论 $i$ 星球与之前的势力是否相同:

  1. 不同,即 $j=1$:

状态可以通过 $dp[i-1][j](l≤j≤r)$ 的状态得到,

也就是枚举 $j$,$dp[i][1][0/1]=dp[i-1][j][1/0]$(势力不同)$+a_i/b_i$(根据 $dp[i][1][0/1]$ 的第三维下标决定。)

  1. 相同,即 $2≤j≤n$

状态的值可以由 $dp[i-1][j-1]$ 的状态得到。

因为如果势力相同,那之前($i-1$)的势力长度就是 $j-1$。

所以 $dp[i][j][0/1]=dp[i-1][j-1][0/1]+a_i/b_i$。

#include <iostream>
#include <cstring>
#define inf 1000000007
using namespace std;
int n, l, r, a[1050], b[1050], ans = inf, dp[1050][1050][2];
int main()
{
    cin >> n >> l >> r;
    for(int i = 1;i <= n;++i)
        cin >> a[i];
    for(int i = 1;i <= n;++i)
        cin >> b[i];
    for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
            dp[i][j][0] = dp[i][j][1] = inf;
    dp[1][1][0] = a[1];
    dp[1][1][1] = b[1];
    for(int i = 2;i <= n;++i)
    {
        for(int j = l;j <= r;++j)
        {
            dp[i][1][0] = min(dp[i][1][0], dp[i - 1][j][1] + a[i]);
            dp[i][1][1] = min(dp[i][1][1], dp[i - 1][j][0] + b[i]);
        }
        for(int j = 2;j <= r;++j)
        {
            dp[i][j][0] = dp[i - 1][j - 1][0] + a[i];
            dp[i][j][1] = dp[i - 1][j - 1][1] + b[i];
        }
    }
    for(int i = l;i <= r;++i)
        ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
    if(ans > 1000000) cout << "NO ANSWER";
    else cout << ans;
    return 0;
}
posted @ 2021-09-25 20:44  5k_sync_closer  阅读(2)  评论(0编辑  收藏  举报  来源