7.19练习题解

传送门

题解

A

解法

手推大样例得到作法:
先从当前状态变成所有对角线都是从一发出的状态,再从这个状态变成答案状态即可。

code

\(\tiny 没有诶嘿嘿\)

B

解法:

考虑暴力枚举。
\(O(n^2m^2)\) 的时间枚举两个装置的位置,再用 \(O(k)\) 的时间计算答案。
复杂度 \(O(n^2m^2k)\)
然后发现跑满有 \(10^9\),再加上xyd的评测姬菜得要死,这个做法会被卡成70分。
然后就可以考虑优化。我们现在确定了一个装置 \(A\) 的位置,并对 \(A\) 求答案。
可以先用一个set存装置放各种位置的答案,再用这个去加上 \(A\) 的答案。
但是有些情况与 \(A\) 会有重复,答案会改变,不能直接加。于是我们考虑枚举与 \(A\) 有重复的情况。
不难发现,我们只用枚举 \(A\) 的哪一个位置和另一个装置的哪一个位置冲突即可,即 \(O(k^2)\)
这时我们将set中另一个装置原来的答案删去再算出他与 \(A\) 冲突完的答案,在 \(A\) 计算完了答案后再把另一个答案在set中的答案还原即可。

总结:\(O(nm)\) 枚举 \(A\) 的位置,用 \(O(k^2)\) 的时间枚举另一个装置的位置并用 \(O(\log n)\) 的时间更改set中的答案(这个复杂度最后被覆盖了),最后用 \(O(k)\) 的时间计算答案,总复杂度 \(O(nmk^3)\)

code

\(\tiny 别想贺代码自己写去吧诶嘿嘿\)

C

签到题。
\(\tiny 没签到555\)

解法:

动态规划。设 \(dp[i]\) 为前 \(i\) 个最优的答案,\(s[i]\) 为前缀和。
在不选 \(a[i]\)

\[dp[i]=dp[i-1] \]

在选 \(a[i]\) 时,考虑枚举 \(j+2\) 作为这一段的开头,所以可以从 \(dp[j]\) 转移。

\[dp[i]=\max_{j=i-k-1}^{i-2}dp[j]+s[i]-s[j+1] \]

\[dp[i]=s[i]+\max_{j=i-k-1}^{i-2}dp[j]-s[j+1] \]

其中 \(dp[j]-s[j+1]\) 直接线段树维护即可,复杂度 \(O(n\log n)\)
当然你想的话可以用单调队列直接 \(O(n)\)

code

\(\tiny 太好写了不放了诶嘿嘿\)

D

神秘二分答案

解法

二分答案。二分最大美观度 \(x\)不(hen)难发现对于任何 \(i\),只要 \(a[i]\le \frac{x}{2}\)\(a[i]\) 就一定可以选。剩下的数可以在两个已选的 \(a[i]\) 中间夹着,但两个已选的中间最多夹一个。大小判断一下即可。

code

终于有了!

//
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e5+5;

int a[MAXN];
int tid,n,m;
vector<int> v;

bool check(int x){
    v.clear();
    for (int i=1;i<=n;i++)
        if (a[i]<=x/2)
            v.push_back(i);

    if (v.empty()){
        bool fg=0;
        for (int i=1;i<=n;i++)
            if (a[i]<=x)
                fg=1;
        return (m<=fg);
    }
    else{
        int cnt=0;
        for (int i=1;i<v.size();i++){
        	int minn=1e18;
        	for (int j=v[i-1]+1;j<v[i];j++) minn=min(minn,a[j]);
            if (minn+a[v[i]]<=x and minn+a[v[i-1]]<=x)
                cnt++;
        }
        int minn=1e18;
        for (int i=1;i<v[0];i++) minn=min(minn,a[i]);
        for (int i=v[v.size()-1]+1;i<=n;i++) minn=min(minn,a[i]);
        if (minn+a[v[0]]<=x and minn+a[v[v.size()-1]]<=x) cnt++;
        return (cnt+v.size()>=m);
    }
}

signed main(){
    // freopen("necklace.in","r",stdin);
    // freopen("necklace.out","w",stdout);
	scanf("%lld %lld %lld",&tid,&n,&m);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    int l=0,r=2e9,ans=0;
    while (l<=r){
        int mid=(l+r)/2;
        if (check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%lld",ans);

	return 0;
}
posted @ 2025-07-21 10:48  sean0613  阅读(28)  评论(1)    收藏  举报
//雪花飘落效果