跳转至

Codeforces Rating 1900+ 题目练习

2025-04-20

早该多刷 1900 的题目的,虽然 1600 也没太做熟,重点是做。不会就早看题解。不过当前是先按人多的刷,不然会很吃力。

人多的可能比较板,更容易有成就感,可能也会比较有教育意义。而且数量也不会很多。

通过时间为倒序,上面的会是新写的,下面的是旧的。

CF600E *2300

2025-04-30

从这题决定看线段树合并的, 现在懂得差不多了。

这题是给一棵树, 每个节点会有一个颜色 \(c\) , 我们需要计算每个子树中的出现次数最多的颜色的和。

这个题我们可以用线段树去维护每种颜色出现的次数。

我们对树进行 dfs, 那么每个节点只需要合并它的每个子结点的线段树, 就可以知道这颗子树的信息。

push up

这里是看左边和右边, 哪边出现的次数更多, 来决定如何合并。 这个还是比较显然的, 这里要理解左右孩子实际上是左右的区间, 是颜色的信息。 比如 \([l, r]\) 之间的颜色出现的次数以及答案。

update

实际上这题这涉及到单点修改, 如果这个点是空的, 肯定给它分配一个, 然后递归去改, 这里改的就是说给这个值的数量去增加。 改完记得 push up , 跟普通线段树一样的。

merge

merge 函数是用于合并线段树的, 一般是把 \(b\) 给合并到 \(a\) 上, 递归合并, 若果有一个是空的, 肯定分配非空的那个, 到叶子节点时, 就是进行一个单点的合并。

constexpr int N = int(1e5) + 5;
struct tree{
    int l, r, cnt;
    ll ans;
    tree(int v = 0){
        l = r = cnt = ans = v;
    }
};
tree tr[N * 50];
#define mi ((l + r) >> 1)
#define lson tr[p].l
#define rson tr[p].r
int cnt = 0, rt[N];

void push_up(int p)
{
    tr[p].cnt = max(tr[lson].cnt, tr[rson].cnt);
    if(tr[lson].cnt > tr[rson].cnt)
        tr[p].ans = tr[lson].ans;
    else if(tr[lson].cnt < tr[rson].cnt)
        tr[p].ans = tr[rson].ans;
    else
        tr[p].ans = tr[lson].ans + tr[rson].ans;
}
void update(int &now, int l, int r, int pos, int v)
{
    if(!now) now = ++cnt;
    if(l == r){
        // 到叶子节点了
        tr[now].cnt += v;
        tr[now].ans = l;
        return;
    }
    // 递归处理
    if(pos <= mi) update(tr[now].l, l, mi, pos, v);
    else update(tr[now].r, mi + 1, r, pos, v);
    push_up(now);
}
int merge(int a, int b, int l, int r)
{
    if(!a) return b;
    if(!b) return a;
    if(l == r){
        tr[a].cnt += tr[b].cnt;
        tr[a].ans = l;
        return a;
    }
    tr[a].l = merge(tr[a].l, tr[b].l, l, mi);
    tr[a].r = merge(tr[a].r, tr[b].r, mi + 1, r);
    push_up(a);
    return a;
}
void ChatGptDeepSeek() // Date: 2025-04-29
{                      // Time: 23:52:50 
    int n;
    cin >> n;
    cnt = n;
    vi c(n + 1);
    vl ans(n + 1);
    for(int i = 1; i <= n; i++){
        cin >> c[i];
        rt[i] = i;
    }
    vector<vi> g(n + 1, vi());
    for(int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    auto dfs = [&](auto &&self, int u, int pre) -> void{
        for(auto v : g[u]){
            if(v == pre) continue;
            self(self, v, u);
            rt[u] = merge(rt[u], rt[v], 1, 100000);
        }
        update(rt[u], 1, 100000, c[u], 1);
        ans[u] = tr[rt[u]].ans;
    };
    dfs(dfs, 1, 0);
    for(int i = 1; i <= n; i++)
        cout << ans[i] << " \n"[i == n];
}

CF86D *2200

2025-04-25

莫队算法模板题, 去学了一下发现学个模板还是很快的。

constexpr int N = int(1e6)+5;
int cnt[N];
struct node{
    int l, r, idx;
};
node Q[N];
void ChatGptDeepSeek() // Date: 2025-04-25
{                      // Time: 21:27:09 
    int n, q;
    cin >> n >> q;
    vi a(n + 1), pos(n + 1);
    vl ans(q + 1);
    int siz = sqrt(n);
    for(int i = 1; i <= n; i++)
        cin >> a[i], pos[i] = i / siz;
    // vector<node>Q(q);
    for(int i = 0; i < q; i++){
        cin >> Q[i].l >> Q[i].r;
        Q[i].idx = i;
    }
    sort(Q, Q + q, [&](node x, node y){
        return (pos[x.l] == pos[y.l]) ? (x.r < y.r) : (pos[x.l] < pos[y.l]);
    });
    // sort(all(Q), [&](node x, node y){
    //     return (pos[x.l] == pos[y.l]) ? (x.r < y.r) : (pos[x.l] < pos[y.l]);
    // });
    ll res = 0;
    auto add = [&](int i){
        res -= (ll)cnt[a[i]] * cnt[a[i]] * a[i];
        cnt[a[i]]++;
        res += (ll)cnt[a[i]] * cnt[a[i]] * a[i];
    };
    auto sub = [&](int i){
        res -= (ll)cnt[a[i]] * cnt[a[i]] * a[i];
        cnt[a[i]]--;
        res += (ll)cnt[a[i]] * cnt[a[i]] * a[i];
    };
    int L = 1, R = 0;
    for(int i = 0; i < q; i++){
        auto [l, r, idx] = Q[i];
        while(L > l) add(--L);
        while(R < r) add(++R);
        while(L < l) sub(L++);
        while(R > r) sub(R--);
        ans[idx] = res;
    }
    for(int i = 0; i < q; i++)
        cout << ans[i] << "\n";
} 

CF5C *1900

2025-04-21

这题评分完全瞎搞吧,这题完全应该是 1300 吧。。虽然不妨碍我 WA 两次。我是正着找一遍逆着找一遍,其实也不需要。但简单题也没必要多看了。

void ChatGptDeepSeek() // Date: 2025-04-21
{                      // Time: 17:32:33 
    string s;
    cin>>s;
    int now=0,cnt=1,len=0,cur_len=0;
    for(auto x:s){
        cur_len++;
        if(x=='(') now++;
        else now--;
        if(now<0){
            now=0;
            cur_len=0;
        }else if(now==0){
            if(cur_len>len){
                cnt=1;
                len=cur_len;
            }else if(cur_len==len)
                cnt++;
        }
    }
    int ans_len=len,ans_cnt=cnt;
    now=0,cnt=1,len=0,cur_len=0;
    reverse(all(s));
    for(auto x:s){
        cur_len++;
        if(x=='(') now++;
        else now--;
        if(now>0){
            now=0;
            cur_len=0;
        }else if(now==0){
            if(cur_len>len){
                cnt=1;
                len=cur_len;
            }else if(cur_len==len)
                cnt++;
        }
    }
    if(len>ans_len) cout<<len<<" "<<cnt<<'\n';
    else if(len==ans_len&&cnt>ans_cnt) cout<<len<<" "<<cnt<<'\n';
    else cout<<ans_len<<" "<<ans_cnt<<'\n';
}

CF380C *2000

2025-04-20

给一个括号串,每次询问区间的最长的合法括号子序列。没想到这也能线段树做哇。记的是每个节点未匹配的左括号和右括号的数量,然后左子节点的左括号可以和右子节点的右括号匹配。

string s;
#define ls p<<1
#define rs p<<1|1
#define mi ((l+r)>>1)

constexpr int N = int(1e6)+5;
struct node{
    int l,r;
};
node tr[N<<2];

node merge(node x,node y){
    node res;
    res.l=x.l+y.l-min(x.l,y.r);
    res.r=x.r+y.r-min(x.l,y.r);
    return res;
}
void build(int p,int l,int r){
    if(l==r){
        if(s[l]=='(') tr[p]={1,0};
        else tr[p]={0,1};
        return;
    }
    build(ls,l,mi),build(rs,mi+1,r);
    tr[p]=merge(tr[ls],tr[rs]);
}
node query(int p,int l,int r,int lx,int rx)
{
    if(l>=lx&&r<=rx) return tr[p];
    node res{0,0};
    if(lx<=mi) res=merge(res,query(ls,l,mi,lx,rx));
    if(rx>mi) res=merge(res,query(rs,mi+1,r,lx,rx));
    return res;
}
void ChatGptDeepSeek() // Date: 2025-04-16
{                      // Time: 11:32:28 
    cin>>s;
    int n=sz(s);
    s=" "+s;
    build(1,1,n);
    int m; cin >> m;
    while(m--){
        int l,r; cin>>l>>r;
        node res=query(1,1,n,l,r);
        cout<<r-l+1-res.l-res.r<<'\n';
    }
}