Codeforces Round 1039 (Div. 2)(A~E1)

A.Recycling Center

思路:n给的很小,所以考虑贪心.如果还剩重量小于等于c的垃圾袋,直接选他们重量最大的,否则剩下的未选垃圾袋都要一个硬币

#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll                                long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
    // ll p,w,c;
    ll c,d;
    friend bool operator<(const s&a,const s&b){
        return a.c>b.c;
    }
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
//     vector<bool>vis(x+5);
//     for(ll i=2;i<=x;i++)
//     {
//         if(!vis[i])zs[++cnt]=i;
//         for(ll j=1;i*zs[j]<=x;j++)
//         {
//             vis[i*zs[j]]=1;
//             if(i%zs[j]==0)break;
//         }
//     }
// }
int main(){
 fio();
ll t=1;
cin>>t;
while(t--){
    ll n,c;
    cin>>n>>c;
    vector<pair<ll,ll>>a(n+2);
    vector<bool>vis(33,0);
    for(ll i=1,x;i<=n;i++){
        cin>>x;
        a[i]={x,i};
    }
    // ll cc=n;
    ll ans=0;
    ll cc=n;
    while(1){
        sort(a.begin()+1,a.begin()+1+n);
        bool flag=0;
        ll id=0;
        for(ll i=1;i<=n;i++){
            if(vis[a[i].second])continue;
            else if(a[i].first<=c){
                id=i;
            }
        }
        if(id){
            vis[a[id].second]=1;   
            for(ll i=1;i<=n;i++)a[i].first*=2;
        }
        else {
            ans+=cc;
            break;
        }
        cc--;
    }
    cout<<ans<<endl;
}
 return 0;
}

B.Deque Process

思路:可以选择类似震荡的构造,就是忽然大忽然小,这个是一定对的,因为所选的最左的数和最右的数大小不一样,那么这次选了,那么下次更大或者更小的数将改变震荡方向。所以逢奇选最大的数,逢偶选最小的数即可

#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll                                long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
    // ll p,w,c;
    ll c,d;
    friend bool operator<(const s&a,const s&b){
        return a.c>b.c;
    }
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
//     vector<bool>vis(x+5);
//     for(ll i=2;i<=x;i++)
//     {
//         if(!vis[i])zs[++cnt]=i;
//         for(ll j=1;i*zs[j]<=x;j++)
//         {
//             vis[i*zs[j]]=1;
//             if(i%zs[j]==0)break;
//         }
//     }
// }
int main(){
 fio();
ll t=1;
cin>>t;
while(t--){
    ll n;
    cin>>n;
    vector<ll>a(n+2);
    for(ll i=1;i<=n;i++){
        cin>>a[i];
    }
    ll l=1,r=n;
    for(ll i=1;i<=n;i++){
        if(i&1){
            if(a[l]>a[r])cout<<"L",l++;
            else cout<<"R",r--;
        }
        else {
             if(a[l]>a[r])cout<<"R",r--;
            else cout<<"L",l++;
        }
    }
    cout<<endl;
}
 return 0;
}

C. Leftmost Below

思路:无论如何,当我要构成第i位的正确答案时,前i-1位必须已经正确。随后我们可以发现,其增加规律是x,x+1,x+x+1+1,x+x+1+1+1...最小递增规律。显然如果我选则x,x+1且x*2+1等于a[i]将是最优秀的叠加方法(其他的要么构造不出来,要么会出现一个数大于此时选的两个数,那还不如这样更优),直接分奇偶模拟即可(注意结合前面的最小值约束)

证明:假设limit很大。1.如果第a[i-1]大于等于a[i],我们先构造好a[i],那么a[i-1]一定已经加了某些值(不妨设为x),下一次加的值最小是x+1,那么这仍然符合上述思路给出的递增规律,如果有个值太大将会使a[i]变大,那最优秀还是用类似一半一半的加法是最优的。如果此时x+x+1会等于a[i],x+1<=a[i],那么确实可以先构造好a[i]然后构造ai-1,但是这与先构造a[i-1],然后构造a[i]没有区别。如果x+1>a[i]显然GG,但是原本我是可以先构造a[i-1],再构造a[i]的,所以此时必须先构造好a[i-1],然后构造a[i]才行;2.如果a[i-1]小于a[i],能先构造出a[i],然后构造出a[i-1]的情况不可能出现(请自己枚举下如6,7。7,8.细节其实还是和递增规律有关)。所以直接构造a[i-1],再构造a[i]是必然的。综上如果答案正确(可以推广到前面去),先构造第i-1位再构造第i位是一定对的,那么推广下,思路中的命题成立。limit只会限制你加多少(根据递增规律来看,最好构造且符合答案的递加方法是类似于一半一半的加)。

吐槽:把等于的情况判断错了,wa了一发,还想了半天为什么不对QwQ

#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll                                long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
    // ll p,w,c;
    ll c,d;
    friend bool operator<(const s&a,const s&b){
        return a.c>b.c;
    }
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
//     vector<bool>vis(x+5);
//     for(ll i=2;i<=x;i++)
//     {
//         if(!vis[i])zs[++cnt]=i;
//         for(ll j=1;i*zs[j]<=x;j++)
//         {
//             vis[i*zs[j]]=1;
//             if(i%zs[j]==0)break;
//         }
//     }
// }
int main(){
 fio();
ll t=1;
cin>>t;
while(t--){
    ll n;
    cin>>n;
    vector<ll>a(n+2);
    for(ll i=1;i<=n;i++)cin>>a[i];
    ll limit=1e18;
    bool flag=0;
    for(ll i=1;i<=n;i++){
        if(a[i]>limit){
            if(a[i]&1){
                if(a[i]/2>limit||(a[i]/2+1)>limit){
                    flag=1;
                    break;
                }
            }
            else {
                if(a[i]/2-1>limit||a[i]/2+1>limit){
                    flag=1; 
                    break;
                }
            }
        }
        limit=min(limit,a[i]);
    }
    if(flag)cout<<"NO"<<endl;
    else cout<<"YES"<<endl;
}
 return 0;
}

D.Sum of LDS

思路:有点小诈骗?,考虑题目给的条件max(\(p_{i-2}\),\(p_{i-1}\))>\(p_i\)。通过简单枚举下我们可以发现,可以转为dp问题(第i位只需从i-1转或者i-2转)。设dp[i]为所选右边界为i,左边界任选的所有答案数。如果第i位前面两个数无比它大的数,此时i=1或者i=2,此时dp[i]直接算即可(不用我说吧)。否则接看位置:如果是\(p_{i-1}>p_i\),那么dp[i]=max(dp[i],dp[i-1]+i-1+1)(直接延长上一位的边界+单独的[i,i]).如果是\(p_{i-2}>p_i\),那么dp[i]=max(dp[i],dp[i-2]+i-2+2)(延长位置为i-2的答案,然后考虑[i-1,i],[i,i],这两个答案为1).然后边走边加上答案即可。

#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll                                long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
    // ll p,w,c;
    ll c,d;
    friend bool operator<(const s&a,const s&b){
        return a.c>b.c;
    }
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
//     vector<bool>vis(x+5);
//     for(ll i=2;i<=x;i++)
//     {
//         if(!vis[i])zs[++cnt]=i;
//         for(ll j=1;i*zs[j]<=x;j++)
//         {
//             vis[i*zs[j]]=1;
//             if(i%zs[j]==0)break;
//         }
//     }
// }
int main(){
 fio();
ll t=1;
cin>>t;
while(t--){
    ll n;
    cin>>n;
    vector<ll>a(n+3);
    for(ll i=1;i<=n;i++)cin>>a[i];
    vector<ll>dp(n+3,0);
    // dp[0]=1;
    ll ans=0;
    for(ll i=1;i<=n;i++){
        if(i==1){
            dp[i]=1;
        }
        else if(i==2){
            if(a[i-1]>a[i])dp[i]=dp[i-1]+2;
            else dp[i]=2;
        }
        else {
            if(a[i-1]>a[i])dp[i]=dp[i-1]+i-1+1;
            if(a[i-2]>a[i])dp[i]=max(dp[i],dp[i-2]+i-2+1+1);
        }
        // cout<<dp[i]<<endl;
        ans+=dp[i];
    }
    cout<<ans<<endl;
}
 return 0;
}

E1. Submedians (Easy Version)

思路:看了会,发现还是二分。我们可以这样,假定一个数mid,然后a数组中的数大于等于它即为1,否则为-1,那么根据题目给出的中位数要求,我们只需要看是否存在长度大于等于k的子数组,且子数组之和的大于等于0.只要存在l就变大,否则r变小。怎么去解决check呢?类似dp的做法,从左往右做个最大连续值dp(设为pre),从右往左做个最大连续dp(设为sub),然后再做个前缀和(设为cc),然后枚举i,取max(cc[i+k-1]-cc[i-1]+pre[i-1]+sub[i+k]),如果最大值大于等于0即可根据刚刚说法得出二分的趋势。如何判定区间,确定好最后答案后,从左往右扫一遍,第一次出现cc[i+k-1]-cc[i-1]+pre[i-1]+sub[i+k]>=0的i即为左边界,然后根据左边界和k的大小遍历找右边界即可。

#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll                                long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
    ll ans = 1;
    while (y)
    {
        if (y & 1)
        {
            ans = ans * x % mod;
        }
        x = x * x % mod;
        y >>= 1;
    }
    return ans % mod;
}
void fio()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
}
struct s {
    // ll p,w,c;
    ll c, d;
    friend bool operator<(const s& a, const s& b) {
        return a.c > b.c;
    }
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
//     vector<bool>vis(x+5);
//     for(ll i=2;i<=x;i++)
//     {
//         if(!vis[i])zs[++cnt]=i;
//         for(ll j=1;i*zs[j]<=x;j++)
//         {
//             vis[i*zs[j]]=1;
//             if(i%zs[j]==0)break;
//         }
//     }
// }
int main() {
    fio();
    ll t = 1;
    cin >> t;
    while (t--) {
        ll n, k;
        cin >> n >> k;
        vector<ll>a(n + 2);
        for (ll i = 1; i <= n; i++) {
            cin >> a[i];
        }
        ll l = 1, r = n;
        ll al=-1;
        function<ll(ll)>ck = [&](ll x) {
            vector<ll>pre(n + 2, 0), sub(n + 2, 0), cc(n + 2, 0);
            for (ll i = 1; i <= n; i++) {
                if (a[i] >= x) {
                    pre[i] = max(pre[i - 1] + 1, 1ll);
                }
                else {
                    pre[i] = max(pre[i - 1] - 1, 0ll);
                }
                cc[i] = cc[i - 1] + (a[i] >= x ? 1 : -1);
            }
            for (ll i = n; i >= 1; i--) {
                if (a[i] >= x) {
                    sub[i] = max(sub[i + 1] + 1, 1ll);
                }
                else {
                    sub[i] = max(sub[i + 1] - 1, 0ll);
                }
            }
            ll d = -1e18;
            for (ll i = 1; i <= n - k + 1; i++) {
                ll r = i + k - 1;
                d = max(pre[i - 1] + sub[r + 1] + cc[r] - cc[i-1], d);
                if(d>=0&&al==-1)al=i;
            }
            return d >= 0;
            };
        while (l <= r) {
            ll mid = l + r >> 1;
            if (ck(mid)) {
                l = mid + 1;
            }
            else r = mid - 1;
        }
        al=-1;
        ll ar;
        ll u=ck(r);
        ll cc=0;
        for(ll i=al;i<=n;i++){
            cc+=(a[i]>=r?1:-1);
            if(i>=al+k-1&&cc>=0){
                ar=i;
                break;
            }
        }
        cout<<r<<" "<<al<<" "<<ar<<endl;
    }
    return 0;
}
posted @ 2025-07-28 00:43  长皆  阅读(192)  评论(2)    收藏  举报