Pinely Round 5 (Div. 1 + Div. 2) A+B+C+D

A.Round Trip


原题链接

解题思路

简单贪心模拟一下,能减则减

AC code

void solve(){
    int r,x,d,n;
    cin>>r>>x>>d>>n;
    string s;
    cin>>s;
    s="&"+s;
    int ans=0;
    for(int i=1;i<=n;i++){
        if(r<x&&s[i]=='2') {
            ans++; 
            r-=d;
        }
        else if(s[i]=='1'){
            ans++; 
            r-=d;
        }
    }
    cout<<ans<<endl;
}

B.Make Connected


原题链接

解题思路

不存在连续三格黑色的,那么
1.每个黑色的联通块每次只能走从上侧主对角线和主对角线 或 下侧主对角线和主对角线(副对角线也可)两种策略到达其他的连通块
2.对于每个黑色块,维护四种行走方案,从主对角线上方走&副对角线上方走,主对角线下方走&副对角线上方走,主对角线下方走&副对角线上方走,
观察四种方案是否存在一种可以走到其他所有黑色块,可以拿set维护
复杂度大概是 \(O(n^2\lg {n})\)

AC code

void solve(){
    int n;cin>>n;
    vector<string>a(n+1,string(n+1,'.'));
    vector<string>vis(n+1,string(n+1,0));
    vector< set< pair<int,int> > >L(n<<2|1);
    vector< set< pair<int,int> > >R(n<<2|1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n-2;j++){
            if(a[i][j]=='#'&&a[i][j+1]=='#'&&a[i][j+2]=='#'){
                cout<<"NO"<<endl;
                return;
            }
        }
    }
    for(int i=1;i<=n-2;i++){
        for(int j=1;j<=n;j++){
            if(a[i][j]=='#'&&a[i+1][j]=='#'&&a[i+2][j]=='#'){
                cout<<"NO"<<endl;
                return;
            }
        }
    }
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(a[i][j]=='#'){
                cnt++;
                L[i+j].emplace(i,j);
                R[i-j+n].emplace(i,j);
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(a[i][j]=='#'){
                set<pair<int,int>>Up,Left,Right,Down,All1,All2,All3,All4;
                for(auto &[x,y]:L[i+j]){
                    Up.emplace(x,y);
                    Left.emplace(x,y);
                    Right.emplace(x,y);
                    Down.emplace(x,y);
                }
                for(auto &[x,y]:R[i-j+n]){
                    Up.emplace(x,y);
                    Left.emplace(x,y);
                    Right.emplace(x,y);
                    Down.emplace(x,y);
                }
                for(auto &[x,y]:L[i+j+1]){
                    Up.emplace(x,y);
                }
                for(auto &[x,y]:L[i+j-1]){
                    Down.emplace(x,y);
                }
                for(auto &[x,y]:R[i-j+n+1]){
                    Right.emplace(x,y);
                }
                for(auto &[x,y]:R[i-j+n-1]){
                    Left.emplace(x,y);
                }
                for(auto &[x,y]:Up){
                    All1.emplace(x,y);
                    All3.emplace(x,y);
                }
                for(auto &[x,y]:Down){
                    All2.emplace(x,y);
                    All4.emplace(x,y);
                }
                for(auto &[x,y]:Left){
                    All1.emplace(x,y);
                    All2.emplace(x,y);
                }
                if(All1.size()==cnt||All2.size()==cnt) continue;
                for(auto &[x,y]:Right){
                    All3.emplace(x,y);
                    All4.emplace(x,y);
                }
                if(All3.size()==cnt||All4.size()==cnt) continue;
                cout<<"NO"<<endl;
                return;
            }
        }
    }
    cout<<"YES"<<endl;
}

C.Loyalty


原题链接

解题思路

观察忠诚度定义,发现可以把区间分成若干块,每一块产生贡献的是块内最后元素
不难想到一种贪心的思路,尽可能产生大的贡献,把若干个最大的值最大的值放在块的最后
然后维护每个最大值,贪心使其产生贡献丢掉前面若干个最小值。最后的答案就是可以产生贡献1的最大值之和。
排序然后双指针维护大概就行了,这里代码为了方便采用了std::multiset.

AC code

void solve(){
    int n;cin>>n;
    ll X;cin>>X;
    vector<ll>a(n+1);
    multiset<ll>Q;
    for(int i=1;i<=n;i++) cin>>a[i],Q.emplace(a[i]);
    ll sum=accumulate(a.begin()+1,a.end(),0LL);
    int cnt=0;
    vector<ll>ans;
    ll tmp=0;
    while(!Q.empty()){
        ll back=*Q.rbegin();
        Q.erase(Q.find(back));
        tmp+=back;
        while(!Q.empty()&&tmp/X<=cnt){
            int front=*Q.begin();
            tmp+=front;
            ans.push_back(front);
            Q.erase(Q.begin());
        }
        if(tmp/X>cnt) cnt=tmp/X;
        ans.push_back(back);
    }
    auto calc=[&](vector<ll>&ans) ->ll {
        ll res=0,tmp=0,cnt=0;
        for(int i=0;i<ans.size();i++){
            tmp+=ans[i];
            if(tmp/X>cnt){
                cnt=tmp/X;
                res+=ans[i];
            }
        }
        return res;
    };
    cout<<calc(ans)<<endl;
    for(int i=0;i<ans.size();i++) cout<<ans[i]<<' ';
    cout<<endl;
}

D.Locked Out


原题链接

解题思路

观察发现答案值域限定非常小,考虑从值域出发枚举,
首先模板化的把删去最少元素转化为保留最多的元素
然后从大到小开始枚举每个值域对应的下标,
定义 $dp_i $为下标为 $i $的合法串的最大长度
当前下标的dp值由
当前元素前面所有比当前下标大的元素 以及
当前元素后面比当前下标大至少2的元素 转移而来
对于第2种 可以用一种延迟更新的Trick,
而第1种直接询问[1,i-1]中最大dp值即可
发现这样做暴力的话是 \(O(n^2)\),拿线段树或树状数组优化一下可以变成 \(O(n \lg n)\)

AC code

struct BIT{
    #define lc (p<<1)
    #define rc (p<<1|1)
    struct Seg{
        int l,r;ll sum;
        Seg():l(0),r(0),sum(0LL){}
        Seg(int l,int r,ll sum):l(l),r(r),sum(sum){}
    };
    int n;
    vector<Seg>tre;
    BIT(int n):n(n){
        tre.resize(n<<2);
        build(1,n,1);
    }
    void pushup(int p){
        tre[p].sum=max(tre[lc].sum,tre[rc].sum);
    }
    void build(int l,int r,int p){
        tre[p]=Seg(l,r,0LL);
        if(l==r) return;
        int mid=l+r>>1;
        build(l,mid,lc);
        build(mid+1,r,rc);
        pushup(p);
    }
    void Upd(int x,ll k,int p){
        if(tre[p].l==tre[p].r){
            tre[p].sum=max(tre[p].sum,k);
            return;
        }
        int mid=tre[p].l+tre[p].r>>1;
        if(mid>=x) Upd(x,k,lc);
        if(mid<x) Upd(x,k,rc);
        pushup(p);
    }
    void Upd(int x,ll k){
        const int p=1;
        if(tre[p].l==tre[p].r){
            tre[p].sum=max(tre[p].sum,k);
            return;
        }
        int mid=tre[p].l+tre[p].r>>1;
        if(mid>=x) Upd(x,k,lc);
        if(mid<x) Upd(x,k,rc);
        pushup(p);
    }
    ll qry(int l,int r,int p){
        if(l<=tre[p].l&&tre[p].r<=r){
            return tre[p].sum;
        }
        int mid=tre[p].l+tre[p].r>>1;
        ll ans=0;
        if(mid>=l) ans=max(ans,qry(l,r,lc));
        if(mid<r) ans=max(ans,qry(l,r,rc));
        return ans; 
    }
    ll qry(int l,int r){
        const int p=1;
        if(l<=tre[p].l&&tre[p].r<=r){
            return tre[p].sum;
        }
        int mid=tre[p].l+tre[p].r>>1;
        ll ans=0;
        if(mid>=l) ans=max(ans,qry(l,r,lc));
        if(mid<r) ans=max(ans,qry(l,r,rc));
        return ans; 
    }
};
void solve(){
    int n;cin>>n;
    vector<int>a(n+1);
    vector<vector<int>>p(n+1);
    for(int i=1;i<=n;i++) {
        cin>>a[i];
        p[a[i]].push_back(i);
    }
    BIT S(n+1);
    for(int x=n;x>=1;x--){
        ll res=S.qry(0,n+1);//全局最大值
        for(int i:p[x]){
            int Len=S.qry(0,i);
            S.Upd(i,Len+1);
        }
        if(res>0) S.Upd(0,res);//延迟更新全局最大值
    }
    cout<<n-S.qry(0,n+1)<<endl;
}

附上超时的BF代码便于理解

void solve() {
    int n;
    cin >> n;
    vector<vector<int>> vec(n+1);
    for (int i = 0; i < n; ++i) {
        int val;
        cin >> val;
        vec[val].push_back(i);
    }
    vector<int> max_vals(n + 1);
    for (int v_idx = n; v_idx >= 1; --v_idx) {
        int res = 0;
        for (int val : max_vals) res = max(res, val);
        for (int i : vec[v_idx]) {
            int prev_max = 0;
            for (int j = 0; j <= i; ++j) {
                 prev_max = max(prev_max, max_vals[j]);
            }
            max_vals[i ] = 1 + prev_max;
        }
        if (res > 0) {
             max_vals[0] = max(max_vals[0], res);
        }
    }
    int lgs_length = 0;
    for (int val : max_vals) {
        lgs_length = max(lgs_length, val);
    }
    cout << n - lgs_length << "\n";
}
posted @ 2025-11-01 17:01  usedchang  阅读(48)  评论(0)    收藏  举报