Codeforces Round 857 (Div. 2) 感想


感想

2/7,笑嘻了,D题忘了一个情况结果后面被卡了,排名直接翻一番,一看周围都是被卡了的哥们。1100的通过直接变600。
C题疑似找规律题,但我不是很懂原理,没写出来,场下照着样例编了一个过的,有个牛人写了个(i >> 32) + j也过了,完全不懂原理。

A. Likes

1.题目内容

题目地址

2.个人思路:

照着题目思路来就行,最大就先正数再负数,最小就正数负数轮换再全放正数。

3.代码:

ll t, m, n, p = 998244353;
ll mix[105];

int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        int ma = 0;
        for (int i = 1; i <= n; i++) {
            cin >> mix[i];
            if (mix[i] > 0) ma++;
        }
        int mi = n - ma, tmp = mi;
        for (int i = 1; i <= n; i++) {
            if (i > ma) ma--;
            cout << (i > ma ? ma : i) << " ";
        }
        cout << "\n";
        int la = 0;
        for (int i = 1; i <= n; i++) {
            if (i <= mi * 2) {
                if (i % 2) cout << 1 << " ";
                else cout << 0 << " ";
                la = i % 2;
            }
            else {
                la++;
                cout << la << " ";
            }
        }
        cout << "\n";
    }
    return 0;
}

B. Settlement of Guinea Pigs

1.题目内容

题目地址

2.个人思路:

还是找规律模拟,在两次遇到医生之间的所有都需要再弄一个鸟笼,遇到医生后就考虑已知性别最坏情况,就是 当前总数/2 + 1,
奇数情况:一定是这个结果,证明省略。
偶数情况:最坏两个性别的笼子内都有奇数个,此时就额外需要一个笼子,所以也是 当前总数/2 + 1。

3.代码:

ll t, m, n, p = 998244353;
ll arr[100005];
 
int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> arr[i];
        }
        int sum = 0, now = 0, res = 0;
        int tot = 0;
        for (int i = 1; i <= n; i++) {
            if (arr[i] == 1) {
                sum++;
                now++;
                tot++;
            }
            else {
                if(tot % 2 == 0 && tot > 0) now = tot / 2 + tot % 2 + 1;
                else now = tot / 2 + tot % 2;
            }
            res = max(res, now);
        }
        cout << res << "\n";
    }
    return 0;
}

C. The Very Beautiful Blanket

1.题目内容

题目地址

2.个人思路:

没有思路,完全不懂,场上也没做出来,代码是我照葫芦画瓢弄得,看个乐呵吧。

3.代码:

ll t, m, n, p = 998244353;
ll mir[305][305];
 
int main() {
    cin >> t;
    while (t--) {
        cin >> n >> m;
        int now = 0;
        for (int i = 1; i <= n; i += 2) {
            now = 512 * (i / 2);
            for (int j = 1; j * 2 <= m + 1; j++) {
                mir[i][j * 2 - 1] = now++;
                mir[i][j * 2] = now++;
                mir[i + 1][j * 2 - 1] = now++;
                mir[i + 1][j * 2] = now++;
            }
        }
        cout << n * m << "\n";
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                cout << mir[i][j] << " ";
            }
            cout << "\n";
        }
    }
    return 0;
}

D. Buying gifts

1.题目内容

题目地址

2.个人思路:

比较好想的dp题,但是有点细枝末节的地方容易遗忘。
先对a进行排序,然后记录b从后到前的最大值,接下来考虑a位于第i个下的情况。假设dp[i]为a中最贵物品为a[i]时的最小差。

此时,b[i](跟着a排序变换过的)前的数字可以任取(包括不取!),另外b[i + 1] ~ b[n]的所有数字是必须取的,所以设max[i + 1] 为 b[i + 1] ~ b[n]的最大值,则在取i之前的数字的情况下:
dp[i] = min(dp[i], abs(a[i] - max(max[i + 1], 1~i - 1中最接近a[i]的)))
如果不取,情况则为:
dp[i] = min(dp[i], abs(a[i] - max[i + 1]))。
至于找最接近的,使用set即可。
这个题关键是考虑没拿b中前边数字的情况,很容易忘记,pretest也没提这茬,然后就挂了。

3.代码:

struct com {
    ll f;
    ll to;
    bool operator < (const com& b) const {
        if (f == b.f) return to > b.to;
        return f < b.f;
    }
}crr[500005];
 
ll t, m, n, p = 998244353;
ll dp[500005], mm[500005];
set<ll> useb;
 
int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        ll ans = 2147483647ll;
        useb.clear();
        for (int i = 1; i <= n; i++) {
            ll a, b;
            cin >> a >> b;
            crr[i] = { a, b };
        }
        mm[n + 1] = 0;
        sort(crr + 1, crr + 1 + n);
        for (int i = n; i > 0; i--) {
            mm[i] = max(crr[i].to, mm[i + 1]);
        }
        useb.insert(crr[1].to);
        ans = abs(crr[1].f - mm[2]);
        for (int i = 2; i <= n; i++) {
            dp[i] = 2147483647ll;
            auto x = useb.lower_bound(crr[i].f);
            if (x != useb.end()) {
                if (mm[i + 1] >= *x) {
                    dp[i] = min(dp[i], abs(crr[i].f - mm[i + 1]));
                }
                else {
                    dp[i] = min(dp[i], abs(crr[i].f - *x));
                }
            }
            if (x != useb.begin()) {
                x--;
                if (mm[i + 1] >= *x) {
                    dp[i] = min(dp[i], abs(crr[i].f - mm[i + 1]));
                }
                else {
                    dp[i] = min(dp[i], abs(crr[i].f - *x));
                }
            }
            if(i != n)
                dp[i] = min(dp[i], abs(crr[i].f - mm[i + 1]));
            ans = min(ans, dp[i]);
            useb.insert(crr[i].to);
        }
        cout << ans << "\n";
    }
    return 0;
}

E. Music Festival

1.题目内容

题目地址

2.个人思路:

考虑将每一张唱片按照最大值从小到大排序,之所以这么做,是因为在加入了最大值为x的唱片后,所有小于x的唱片全部无效,所以最佳策略一定从这里诞生。
还是考虑dp,遍历每一个唱片的每一个曲子的coooool值,以及这张唱片所能贡献的unit of impression,显而易见,这个数字取决于之前序列的最大值,
即第i个位置为 dp[i] = max( dp[j](j < i) + 序列中大于第j个位置所单调递增的序列长度)。
乍一看这个思路需要O(n^2)以上才能处理,但我们可以换一个角度,即每次完成一个唱片后,记录下当前唱片最大值和最大值对应的长度,然后遍历新的唱片曲子,这个时候,我们通过map查询小于当前唱片歌曲值的所保存的长度,再加上从当前位置到最大值的上升序列长度即为所求。
只需要O(nlogn)的时间即可处理,本题需要读入优化,cin会爆。

3.代码:

struct com {
    ll f;
    ll to;
    ll s;
    bool operator < (const com& b) const {
        if (f == b.f) return to > b.to;
        return f < b.f;
    }
}lis[200005];

ll t, m, n, p = 998244353;
ll dp[200005];
map<ll, ll> lim;
vector<ll> alb[200005];
//ll tr[2000005], lz[2000005];

int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        int ans = 0;
        lim.clear();
        for (int i = 1; i <= n; i++) {
            alb[i].clear();
            dp[i] = 0;
            int k;
            scanf_s("%d", &k);
            int f = 0, l = 0;
            for (int j = 0; j < k; j++) {
                int a;
                scanf_s("%d", &a);
                if (a > f) {
                    f = a;
                    l++;
                }
                alb[i].push_back(a);
            }
            lis[i] = { f, l, i };
        }
        sort(lis + 1, lis + 1 + n);
        int now = 0;
        lim.insert({ 0, 0 });
        for (int i = 1; i <= n; i++) {
            int tmp = alb[lis[i].s].size(), x = lis[i].s;
            int nm = 0, ns = 0;
            for (int j = 0; j < tmp; j++) {
                int y = alb[x][j];
                if (y > nm) {
                    nm = y;
                    auto z = lim.lower_bound(y);
                    z--;
                    dp[i] = max(dp[i], z->second + lis[i].to - ns);
                    ns++;
                }
            }
            dp[i] = max(dp[i], dp[i - 1]);
            lim.insert({ lis[i].f, dp[i] });
        }
        cout << dp[n] << "\n";
    }
    return 0;
}

总结

笑嘻了,真的蚌埠住了,C题半天也没想明白,希望有大佬能解释一下吧,我对这个思路只能说神奇。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值