26.3.25 1600-1900 板刷日记

2192 D. Cost of Tree

tag: DFS,树形 DP

对以 \(u\) 为根的树来说,只有三种选择(下面的子树是指:以 \(u\) 的子节点 \(v\) 为根节点的子树):

  1. 不操作,这个答案是可以简单的预处理出来的
  2. 操作,且操作在某个子树内部:直接枚举是哪个子树被操作即可
  3. 操作,且操作在子树外部,这种情况下,一定是直接选择整个子树,接到其他子树的最深处。

对于第三种情况,维护每个子树的最深深度,然后枚举要移动的子树即可,期间需要记录所有子树的深度最大值和次大值

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<vector<int>> g(n+1);
    vector<int> a(n+1);

    for(int i=1;i<=n;i++){
        cin>>a[i];
    }

    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    vector<int> f(n+1),dp(n+1),dep(n+1),mxdep(n+1),sz(n+1);

    auto dfs1=[&](auto dfs,int u,int pre)-> void {
        dep[u]=dep[pre]+1;
        sz[u]=a[u];
        mxdep[u]=dep[u];
        
        for(auto v:g[u]){
            if(v==pre) continue;
            dfs(dfs,v,u);
            sz[u]+=sz[v];
            f[u]+=f[v]+sz[v];
            mxdep[u]=max(mxdep[u],mxdep[v]);
        }
    };

    dfs1(dfs1,1,0);

    auto dfs2=[&](auto dfs2,int u,int pre)-> void {
        vector<int> t;
        dp[u]=f[u];

        for(auto v:g[u]){
            if(v==pre) continue;
            dfs2(dfs2,v,u);
            t.push_back(mxdep[v]);
        }

        sort(t.begin(),t.end(),greater<int>());

        for(auto v:g[u]){
            if(v==pre) continue;
            dp[u]=max(dp[u],f[u]-f[v]+dp[v]);
        }

        if(t.size()<2) return;

        for(auto v:g[u]){
            if(v==pre) continue;
            if(mxdep[v]!=t[0]){
                dp[u]=max(dp[u],f[u]+sz[v]*(t[0]-dep[v]+1));
                
            }
            else{
                dp[u]=max(dp[u],f[u]+sz[v]*(t[1]-dep[v]+1));

            }

        }

    };

    dfs2(dfs2,1,0);

    for(int i=1;i<=n;i++){
        cout<<dp[i]<<" ";
    }
    cout<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

2190 B1. Sub-RBS (Easy Version)

tag: 括号序列,构造

因为原序列是一个合法序列,所以可以考虑直接将第一个 ) 删掉,再将后面只要删除最后一个 ( 即可

注意删除删除最后一个 ( 会更优,仅当第一个 ) 后有至少两个 ( 才会满足

Hard Version DP 不会做

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    string s;
    cin>>s;
    s=" "+s;

    int pos=s.find(')');

    int cnt=0;
    for(int i=pos+1;i<=n;i++){
        if(s[i]=='(') cnt++;
    }

    if(cnt<2) cout<<-1;
    else cout<<n-2;
    cout<<endl;
    
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

2189 D1. Little String (Easy Version)

tag: MEX,数学

对于排列上的 MEX,有一个定理:

对于一个 \(0\)\(n-1\) 的排列,它的某个区间 \([l,r]\) 的 MEX 等于这个这个区间前后缀 (\([1,l),(r,n]\)) 的最小值

所以对于 \(w_i=1\),等价于 \(i\) 是排列的一个前缀或后缀最小值

所以对于 \(w_i=0\),等价于 \(i\) 不是排列的一个前缀或后缀最小值

特殊的,\(w_0=0\)\(w_n=0\) 不存在

填数字的时候从大到小填,设此时还有 \(t\) 个数字没填,如果这个数字需要是一个最小值(\(2\) 个位置),则将其填到两边,否则填到中间 (\(t-2\) 个位置)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 1e9+7;

void solve(){
    int n,c;
    cin>>n>>c;

    string s;
    cin>>s;
    s=" "+s;

    vector<int> a(n+1);
    for(int i=1;i<=n;i++){
        a[i]=s[i]-'0';
    }

    if(a[1]==0 || a[n]==0){
        cout<<-1<<endl;
        return;
    }

    int ans1=1,ans2=1;
    int now=n-2;

    for(int i=n-1;i;i--){
        if(s[i]=='0'){
            ans1=ans1*now%mod;
            ans2=ans2*now%c;
        }
        else{
            ans1=ans1*2%mod;
            ans2=ans2*2%c;
        }
        now--;
    }

    if(ans2==0) cout<<-1<<endl;
    else cout<<ans1<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

2184 G. Nastiness of Segments

tag:二分+线段树,线段树二分

本题时间宽松, \(O(n*log^2n)\) 的二分+线段树也可通过,不必线段树上二分

对于 \(d\),是单调递增的,而 \(min\) 一定是递减的,所以一定是最多只一个交界点,二分交界点即可,线段树维护

最后再 \(check\) 一下,以防没有交界点

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

class SegmentTree {
public:
    #define lc u<<1
    #define rc u<<1|1

    struct Node {
        int l, r, sum;
        int maxv, minv;
        int add, cov;
        bool has_cov; // 标记是否存在覆盖操作,因为覆盖值可能是 0
    };

    int n;
    vector<int> w;
    vector<Node> tr;

    SegmentTree(int n) {
        init(n);
    }

    void init(int n) {
        this->n = n;
        w.assign(n + 10, 0);
        tr.resize(4 * n + 10);
    }

    void pushup(int u) {
        tr[u].sum = tr[lc].sum + tr[rc].sum;
        tr[u].maxv = max(tr[lc].maxv, tr[rc].maxv);
        tr[u].minv = min(tr[lc].minv, tr[rc].minv);
    }

    // 辅助函数:处理覆盖逻辑
    void apply_cov(int u, int k) {
        tr[u].cov = k;
        tr[u].has_cov = true;
        tr[u].add = 0; // 覆盖后,之前的加法标记失效
        tr[u].sum = (tr[u].r - tr[u].l + 1) * k;
        tr[u].maxv = k;
        tr[u].minv = k;
    }

    // 辅助函数:处理加法逻辑
    void apply_add(int u, int k) {
        tr[u].add += k;
        tr[u].sum += (tr[u].r - tr[u].l + 1) * k;
        tr[u].maxv += k;
        tr[u].minv += k;
    }

    void pushdown(int u) {
        if (tr[u].has_cov) {
            apply_cov(lc, tr[u].cov);
            apply_cov(rc, tr[u].cov);
            tr[u].has_cov = false;
            tr[u].cov = 0;
        }
        if (tr[u].add) {
            apply_add(lc, tr[u].add);
            apply_add(rc, tr[u].add);
            tr[u].add = 0;
        }
    }

    void build(int u, int l, int r) {
        tr[u] = {l, r, 0, 0, 0, 0, 0, false};
        if (l == r) {
            tr[u].sum = tr[u].maxv = tr[u].minv = w[l];
        } else {
            int mid = l + r >> 1;
            build(lc, l, mid);
            build(rc, mid + 1, r);
            pushup(u);
        }
    }

    // 区间加
    void modify_add(int u, int l, int r, int k) {
        if (l <= tr[u].l && r >= tr[u].r) {
            apply_add(u, k);
        } else {
            pushdown(u);
            int mid = tr[u].l + tr[u].r >> 1;
            if (l <= mid) modify_add(lc, l, r, k);
            if (r > mid) modify_add(rc, l, r, k);
            pushup(u);
        }
    }

    // 区间覆盖
    void modify_cov(int u, int l, int r, int k) {
        if (l <= tr[u].l && r >= tr[u].r) {
            apply_cov(u, k);
        } else {
            pushdown(u);
            int mid = tr[u].l + tr[u].r >> 1;
            if (l <= mid) modify_cov(lc, l, r, k);
            if (r > mid) modify_cov(rc, l, r, k);
            pushup(u);
        }
    }

    int query_sum(int u, int l, int r) {
        if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum;
        pushdown(u);
        int res = 0, mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) res += query_sum(lc, l, r);
        if (r > mid) res += query_sum(rc, l, r);
        return res;
    }

    int query_max(int u, int l, int r) {
        if (l <= tr[u].l && r >= tr[u].r) return tr[u].maxv;
        pushdown(u);
        int res = -2e18, mid = tr[u].l + tr[u].r >> 1; // 假设使用 long long
        if (l <= mid) res = max(res, query_max(lc, l, r));
        if (r > mid) res = max(res, query_max(rc, l, r));
        return res;
    }

    int query_min(int u, int l, int r) {
        if (l <= tr[u].l && r >= tr[u].r) return tr[u].minv;
        pushdown(u);
        int res = 2e18, mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) res = min(res, query_min(lc, l, r));
        if (r > mid) res = min(res, query_min(rc, l, r));
        return res;
    }
};

void solve(){
    int n,q;
    cin>>n>>q;

    SegmentTree tr(n);
    for(int i=1;i<=n;i++){
        cin>>tr.w[i];
    }
    
    tr.build(1,1,n);

    while(q--){
        int ch;
        cin>>ch;

        if(ch==1){
            int i,x;
            cin>>i>>x;
            tr.modify_cov(1,i,i,x);
        }
        else{
            int a,b;
            cin>>a>>b;
            int l=0,r=b-a;

            while(l<r){
                int mid=(l+r+1)/2;
                if(tr.query_min(1,a,a+mid)<mid) r=mid-1;
                else l=mid;
            }

            if(tr.query_min(1,a,a+l)==l) cout<<1<<endl;
            else cout<<0<<endl;
        }
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

2185 E. The Robotic Rush

tag: 模拟,排序

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n,m,k;
    cin>>n>>m>>k;

    vector<int> a(n+1),b(m+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=m;i++){
        cin>>b[i];
    }
    sort(b.begin()+1,b.end());
    b[0]=-inf;
    b.push_back(inf);

    vector<pii> l(n+1),r(n+1);

    for(int i=1;i<=n;i++){
        int it=upper_bound(b.begin()+1,b.end(),a[i])-b.begin();
        r[i]={b[it]-a[i],i};
        it=lower_bound(b.begin()+1,b.end(),a[i])-b.begin()-1;
        l[i]={a[i]-b[it],i};
    }

    sort(l.begin()+1,l.end(),greater<pii>());
    sort(r.begin()+1,r.end(),greater<pii>());

    int L=0,R=0;
    int now=0;
    int ans=n;

    vector<int> st(n+1,1);
    
    while(k--){
        char ch;
        cin>>ch;
        if(ch=='L'){
            now--;
        }
        else now++;

        L=max(L,-now);
        R=max(R,now);

        while(l.size()>1 && L>=l.back().first){
            auto [_,id]=l.back();
            l.pop_back();
            if(st[id]){
                // if(ans==0) cout<<id<<" "<<st[id]<<endl;
                ans--;
                st[id]=0;
                // if(ans==-1) cout<<id<<" "<<st[id]<<endl;
            }
        }
        while(r.size()>1 && R>=r.back().first){
            auto [_,id]=r.back();
            r.pop_back();
            if(st[id]){
                ans--;
                st[id]=0;
            }
        }

        cout<<ans<<" ";
    }
    cout<<endl;

}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

2185 F. Cherry Tree

tag:树形 DP

\(f[u][j:0/1/2]\) 表示,对于以 \(u\) 为根的子树,可以不可以用 \(j\) 次操作处理掉子树内的

转移一下子树的 \(DP\) 值即可

另一个思路是,统计叶节点个数,比如有 \(4\) 个,就尝试找一下有没有一个节点可以直接处理两个叶节点

或者有没有 \(2\) 个不相交且都可以处理 \(3\) 个叶节点的节点

但这个思路需要注意很多细节,很难写。

代码是 DP 写法

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<vector<int>> g(n+1);
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    int sum=0;
    vector<int> w(n+1);

    for(int i=2;i<=n;i++){
        if(g[i].size()==1){
            sum++;
            w[i]=1;
        }
    }

    sum%=3;
    if(sum==0){
        cout<<"YES\n";
        return;
    }

    auto dfs1=[&](auto dfs1,int u,int pre)-> void {
        for(auto v:g[u]){
            if(v==pre) continue;
            dfs1(dfs1,v,u);
            w[u]+=w[v];
            w[u]%=3;
        }
    };

    dfs1(dfs1,1,0);

    auto dfs2=[&](auto dfs2,int u,int pre)-> vector<int> {

        vector<int> st(3);

        if(u!=1 && g[u].size()==1){
            st[1]=1;
            return st;
        }

        st[0]=1;

        for(auto v:g[u]){
            if(v==pre) continue;
            auto f=dfs2(dfs2,v,u);

            vector<int> tmp(3);
            for(int i=0;i<3;i++){
                for(int j=0;j<3;j++){
                    if(f[i] && st[j]){
                        tmp[(i+j)%3]=1;
                    }
                }
            }

            st=tmp;
        }

        st[1]=1;
        return st;
    };

    auto st=dfs2(dfs2,1,0);

    if(st[0]) cout<<"YES\n";
    else cout<<"NO\n";

}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}
posted @ 2026-03-27 21:39  LYET  阅读(5)  评论(0)    收藏  举报