2024牛客暑期多校训练营2

E、C、H、B

总结

成绩:SOLVE:3,Penalty:5

E

签到题

取 $x − lowbit(x)$ 即可,如果 $x$ 是 2 的整数次幂则无解

C

从左向右DP即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void solve()
{
    int n;cin>>n;
    string a,b;
    cin>>a>>b;
    int ans=0;
    for(int i=0;i<n;i++){
        dp[0][i]=0;
        dp[1][i]=0;
    }
    a=' '+a;b=' '+b;
    for(int i=1;i<=n;i++){
         
        if(a[i]=='R'){
            dp[0][i]=dp[0][i-1]+1;
        }
        if(b[i]=='R'){
            dp[1][i]=dp[1][i-1]+1;
        }
        int x=dp[0][i],y=dp[1][i];
        if(a[i]=='R'){
            dp[0][i]=max(dp[0][i],y+1);
        }
        if(b[i]=='R'){
            dp[1][i]=max(dp[1][i],x+1);
        }
        ans=max({ans,dp[1][i],dp[0][i]});
    }
    if(ans==0){
        cout<<"0";
        return ;
    }
    cout<<ans-1<<'\n';
}

H

如果选择的左端点为1,那么利用前缀和记录每一个落点即可

对于左端点非1的点,我们可以转化问题,把1到左端点前面的操作加到终点上,等价于从左端点为1开始取

需要注意的是,这里因为经过了转化,我们需要保证选取的转化前的右端点在左端点的右边,才是合法。

因此,我们记录每一个位置,类似单调队列的思想,删去那些不合法的点即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void SINGLE_TEST()
{
    int n,x,y;
    cin >> n >> x >> y;
    string s;
    cin >> s;
    map<PII, set<int>> mp;
    mp[{0,0}].insert(0);
    if(x==0&&y==0){
        cout<<n*(n+1)/2<<"\n";
        return;
    }
    int tx = 0, ty = 0;
    for (int i = 0; i < n; i++) {
        if(s[i]=='W'){
            ty++;
        }else if(s[i]=='A'){
            tx--;
        }else if(s[i]=='D'){
            tx++;
        }else{
            ty--;
        }
        mp[{tx,ty}].insert(i+1);
    }
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        if(i!=0){
            if(s[i-1]=='W'){
                y++;
            }else if(s[i-1]=='A'){
                x--;
            }else if(s[i-1]=='D'){
                x++;
            }else{
                y--;
            }
        }      
        if(!mp.count({x,y})){
            continue;
        }
        while(!mp[{x,y}].empty()&&*mp[{x,y}].begin()<i){
            mp[{x,y}].erase(*mp[{x,y}].begin());
        }
        if (mp[{x, y}].size()) {
            ans += n-*mp[{x,y}].begin()+1;
        }
    }
    cout << ans << "\n";
}

B

使用Kruskal解决,关键在于边怎么取

考虑到$\sum k\leqslant10^5$

对于点集个数$\geqslant \sqrt{10^5}$,那么边的条数在最坏的情况下有可能到达$10^5$,那我们不妨直接遍历所有的边

这里可以得出一个结论,因为$k$的总数固定,那么点集个数$\geqslant \sqrt{10^5}$的情况一定不会出现很多,具体的,是$\sqrt{10^5}$次,时间复杂度为$O(n\sqrt{n})$

对于点集个数$< \sqrt{10^5}$,这样的情况可能会出现很多,而且边的个数显然并不会占总的边的个数的大部分,因此,我们不妨用$O(k^2)$处理出所有的边,时间复杂度为$\sum k^2\times \frac{q}{k}=\sum kq$最坏的情况下时间复杂度为$O(n\sqrt{n})$

因此,加上并行的Kruskal,总时间复杂度为$O(n\sqrt{n}+mlogm)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
unordered_map<int, int> mp[N];
void SINGLE_TEST() {
    int n, m, q;
    cin >> n >> m >> q;
    for (int i = 0; i < m; i++) {
        cin >> edges[i][1] >> edges[i][2] >> edges[i][0];
        edges[i][1]--;
        edges[i][2]--;
        if (edges[i][1] > edges[i][2]) swap(edges[i][1], edges[i][2]);
    }
    sort(edges, edges + m);
    for (int i = 0; i < m; i++) {
        if (!mp[edges[i][1]].count(edges[i][2])) {
            mp[edges[i][1]][edges[i][2]] = i;
        }
    }
    DSU dsu(n);
    for (int i = 0; i < n; i++) d[i] = INF;
    while (q--) {
        int k;
        cin >> k;
        vector<int> v(k);
        for (int i = 0; i < k; i++) {
            cin >> v[i];
            v[i]--;
            ex[v[i]] = 1;
        }
        ll ans = 0;
        int cnt = 0;
        if (k <= 300) {
            sort(v.begin(), v.end());
            vector<int> tmp;
            for (auto v1 : v) {
                int t = -1;
                for (auto v2 : v) {
                    if (mp[v1].count(v2)) {
                        tmp.push_back(mp[v1][v2]);
                    }
                }
            }
            sort(tmp.begin(), tmp.end());
            for (auto i : tmp) {
                if (dsu.merge(edges[i][1], edges[i][2])) {
                    ans += edges[i][0];
                    cnt++;
                }
            }
        } else {
            for (int i = 0; i < m; i++) {
                if (ex[edges[i][1]] && ex[edges[i][2]]) {
                    if (dsu.merge(edges[i][1], edges[i][2])) {
                        ans += edges[i][0];
                        cnt++;
                    }
                }
            }
        }
        if (cnt < k - 1) ans = -1;
        cout << ans << "\n";
        for (auto v1 : v) {
            dsu.f[v1] = v1;
            dsu.siz[v1] = 1;
            ex[v1] = 0;
        }
    }
}
Licensed under CC BY-NC-SA 4.0
最后更新于 Jul 19, 2024 00:00 UTC
本博客已稳定运行
使用 Hugo 构建
主题 StackJimmy 设计