• Codeforces Round #677 (Div. 3)


    写在前面

    最近开始刷CF,今天第一次补完完整的一套CF,总结一下吧,发现做过的题不赶紧总结写题解就容易忘了。
    立个flag 争取每天更新一篇CF题解

    A. Boring Apartments

    模拟题,模拟就完了

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    int sum, a[100005];
    int main(){
        for (int i = 1; i <= 9;i++){
            for (int j = 1; j <= 4;j++){
                sum += j;
                int temp = i;
                for (int k = 1; k < j;k++){
                    temp = temp * 10 + i;
                }
                a[temp] = sum;
            }
        }
        int n;
        cin >> n;
        while(n--){
            int x;
            cin >> x;
            cout << a[x] << endl;
        }
        return 0;
    }
    

    B. Yet Another Bookshelf

    大意: 1代表有书,0代表没有书,对于每次操作,可以将一段连续的1整体向左或向右移动,问将全部的1移到一起最少要多少步
    思路: 直接计算中间的0有多少个就行

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    int t, n;
    int main(){
        cin>>t;
        while(t--){
            cin>>n;
            int x,flag=0,cnt=0,cnt0=0;
            for (int i = 0; i < n;i++){
                cin >> x;
                if(x==1&&flag==0){
                    flag = 1;
                }
                else if(flag==1&&x==0){
                    cnt++;
                    cnt0++;
                }
                else if(x==1&&flag==1){
                    cnt0 = 0;
                }
            }
            cout << cnt - cnt0 << endl;
        }
        return 0;
    }
    

    C. Dominant Piranha

    大意: 给一个数组a,对于任意一个元素a[i],如果它大于相邻的元素,可以将这个较小的元素吃掉,然后自己的值增加1,问能否找到一个元素,不断吃掉相邻的元素,最终吃掉除自己之外的整个数组。
    思路: 对于一个数组,如果所有的元素都相同,那么必然找不到可以吃的元素;否则就找到最大的元素,如果最大的元素有多个,则找到存在较小相邻元素的那个即可,因为这样它可以通过吃掉这个较小的元素,从而成为唯一的最大的元素。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 3e5 + 5;
    int t, n, maxn, anss,x,a[N],flag=0;
    int main(){
        cin>>t;
        while(t--){
            cin >> n;
            maxn = -1;
            for (int i = 1; i <= n;i++){
                cin >> a[i];
                maxn = max(maxn, a[i]);
            }
            anss = -1;
            for (int i = 1; i <= n;i++){
                if(i==1){
                    if(a[i]==maxn&&a[i+1]!=a[i]){
                        anss = i;
                        break;
                    }
                }
                else if(i==n){
                    if(a[i]==maxn&&a[i-1]!=a[i]){
                        anss = i;
                        break;
                    }
                }
                else{
                    if(a[i]==maxn&&(a[i-1]!=a[i]||a[i+1]!=a[i])){
                        anss = i;
                        break;
                    }
                }
            }
            cout << anss << endl;
        }
        return 0;
    }
    

    D. Districts Connection

    大意: 给出n个数,其中 (a_i)代表第i个街区属于第(a_i)个帮派,相同帮派之间的街区不能直连,问能否得到符合条件的树,使得所有的街区都联通
    思路: 直接构造即可,因为不同的街区之间不能直连,所以将所有和第一个街区所属帮派不同的街区和第一个街区相连,然后将和第一个街区的帮派相同的街区与 和第一个街区相连的一个街区 相连即可。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    int t, n,other,a[5005];
    vector<int> anss[2];
    int main(){
        cin>>t;
        while(t--){
            cin >> n;
            other = 0;
            anss[0].clear();
            anss[1].clear();
            for (int i = 1; i <= n;i++){
                cin >> a[i];
                if(a[i]!=a[1]){
                    anss[0].push_back(i);
                    other = i;
                }
            }
            if(other==0){
                cout << "NO" << endl;
            }
            else{
                cout << "YES" << endl;
                for (int i = 2; i <= n;i++){
                    if(a[i]==a[1]){
                        anss[1].push_back(i);
                    }
                }
                for (int i = 0; i < anss[0].size();i++){
                    cout << 1 << ' ' << anss[0][i] << endl;
                }
                for (int i = 0;i<anss[1].size();i++){
                    cout << other << ' ' << anss[1][i] << endl;
                }
            }
        }
        return 0;
    }
    

    E. Two Round Dances

    大意: 将n个人分成两组,对于每一组都是一个环,问有多少种不同的分法,注意对于一个环来说 1234 和 4123是相同的
    思路: 一开始不会,搜了一下才知道这是圆排列数
    圆排列的定义为: 有一组元素,将其排成一个圆,这种排列叫做圆排列或项链排列。
    一般地,有m个元素作圆排列,其计算公式为 ((m-1)!)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    long long anss=1,n;
    int main(){
        cin>>n;
        for (int i = (n / 2) + 1; i <= n; i++){
            anss *= i;
        }
        for (int i = 1; i <= (n / 2);i++){
            anss /= i;
        }
        for (int i = (n / 2) - 1; i >= 1;i--){
            anss *= i;
        }
        for (int i = (n / 2) - 1; i >= 1;i--){
            anss *= i;
        }
        cout << anss / 2 << endl;
        return 0;
    }
    

    F. Zero Remainder Sum

    大意: 每行只能选择(leftlfloorfrac{m}{2} ight floor)个元素,问选出的元素和满足能被&k&整除的最大值是多少
    思路: 看题解说是经典dp问题,可是我不会qwq....
    (dp[x][y][cnt][rem])代表当前位置为((x,y)) 已经选取了(cnt)个元素且余数是(rem)
    那么
    (dp[1][1][0][0]=0)代表最初的位置
    (dp[nx][ny][cnt][rem] = max(dp[nx][ny][cnt][rem],dp[x][y][cnt][rem])) 表示不取当前元素
    (dp[nx][ny][cnt + 1][(rem+a_{ij})\%k] = max(dp[nx][ny][cnt+1][(rem+a_{ij})\%k],dp[x][y][cnt][rem] + a_{ij})) 表示取当前元素
    然后一行一行的扫过去就行,最后输出(dp[n+1][1][0][0])

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 70 + 5;
    int a[N][N], dp[N][N][N][N], n, m, k;
    int main(){
        cin >> n >> m >> k;
        for (int i = 1; i <= n;i++){
            for (int j = 1; j <= m;j++){
                cin >> a[i][j];
            }
        }
        memset(dp, -1, sizeof(dp));
        dp[1][1][0][0] = 0;
        for (int i = 1; i <= n;i++){
            for (int j = 1; j <= m;j++){
                for (int cnt = 0; cnt <= (m / 2);cnt++){
                    for (int rem = 0; rem < k;rem++){
                        if(dp[i][j][cnt][rem]==-1){
                            continue;
                        }
                        int ii=i, jj=j+1;
                        if(j==m){
                            jj = 1;
                            ii = i + 1;
                        }
                        if(ii==i){
                            dp[ii][jj][cnt][rem] = max(dp[ii][jj][cnt][rem], dp[i][j][cnt][rem]);
                        }
                        else{
                            dp[ii][jj][0][rem] = max(dp[ii][jj][0][rem], dp[i][j][cnt][rem]);
                        }
                        if((cnt+1)<=m/2){
                            if(ii==i){
                                dp[ii][jj][cnt+1][(rem+a[i][j])%k] = max(dp[ii][jj][cnt+1][(rem+a[i][j])%k], dp[i][j][cnt][rem]+a[i][j]);
                            }
                            else{
                                dp[ii][jj][0][(rem+a[i][j])%k] = max(dp[ii][jj][0][(rem+a[i][j])%k], dp[i][j][cnt][rem]+a[i][j]);
                            }
                        }
                    }
                }
            }
        }
        cout << max(0, dp[n + 1][1][0][0]) << endl;
        return 0;
    }
    

    G. Reducing Delivery Cost

    大意: n个点m个边,可以把其中一个边权值变为0,给出k对起点与终点,问这k对点的距离和最小可以是多少
    思路: 首先跑n次堆优化的dijsktra算法,由于数据较小,可以枚举被删除的边,然后暴力算就行
    注意删除一条边((u,v))后,需要用(min(dis[sx][tx], min(dis[sx][u] + dis[v][tx], dis[sx][v] + dis[u][tx])))来更新
    板子是队友的板子,看了好久才明白...

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e3 + 5;
    int n, m, k;
    typedef pair<int, int> PII;
    int e[2*N], ne[2*N],w[2*N], h[N], idx,dis[N][N], st[N],mp[N][N],anss=0x3f3f3f3f;
    struct node
    {
        int a, b, c;
    };
    
    void add(int a, int b, int c) {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
    }
    
    // 堆优化版dijksyta
    int dijkstra(int sx) {
        memset(dis[sx], 0x3f, sizeof dis[sx]);  // 初始化距离为无穷
        memset(st, 0, sizeof st);
        priority_queue<PII, vector<PII>, greater<PII> > q;  // 定义一个按照距离从小到大排序的优先队列,第一维:距离,第二维:点
        dis[sx][sx] = 0;  // 一开始源点距离为0
        q.push({0, sx});  // 把源点信息放入队列
        while (q.size()) {  // 每个点只出入队列一次
            auto t = q.top();
            q.pop();
            
            int distance = t.first, ver = t.second;  // 最小距离和相对应的点
            if (st[ver]) continue;  // 这个操作保证每个点只出入队一次,因为队列里面可能会出现{dis1[3], 3}, {dis2[3], 3}的情况,这样保证dis1[3]<dis2[3]时,3号点只进出入队一次
            st[ver] = 1;  // 标记,因为dijkstra的贪心策略保证每个点只需要进出队一次
            
            for (int i = h[ver]; ~i; i = ne[i]) {  // 遍历ver的邻接点
                int j = e[i];
                if (dis[sx][j] > distance + w[i]) {
                    dis[sx][j] = distance + w[i];
                    q.push({dis[sx][j], j});  // 这里不需要判断st,因为一旦更新发现更小必须放入队列
                }
            }
        }
    }
    
    
    struct road
    {
        int u, v, w;
    };
    
    vector<struct road> r;
    vector<pair<int, int> >q;
    int main(){
        cin >> n >> m >> k;
        memset(h, -1, sizeof h);
        //memset(mp, 0x3f, sizeof mp);  // 初始更新每个点间距离为无穷远
        for (int i = 0; i < m;i++){
            int x, y, z;
            struct road node;
            cin >> node.u >> node.v >> node.w;
            r.push_back(node);
            add(node.u, node.v, node.w);
            add(node.v, node.u, node.w);
            //mp[node.u][node.v] = min(node.w,mp[node.u][node.v]);
        }
        for (int i = 1; i <= n;i++){
            dijkstra(i);
        }
        for (int i = 0; i < k; i++)
        {
            int sx, tx;
            cin >> sx >> tx;
            q.push_back({sx, tx});
        }
        for (int i = 0; i < r.size();i++){
            int temp = 0;
            for (int j = 0; j < q.size();j++){
                int sx = q[j].first;
                int tx = q[j].second;
                temp += min(dis[sx][tx], min(dis[sx][r[i].u] + dis[r[i].v][tx], dis[sx][r[i].v] + dis[r[i].u][tx]));
            }
            anss = min(temp, anss);
        }
        cout << anss << endl;
        return 0;
    }
    
  • 相关阅读:
    cf B. Sereja and Suffixes
    cf E. Dima and Magic Guitar
    cf D. Dima and Trap Graph
    cf C. Dima and Salad
    最短路径问题(floyd)
    Drainage Ditches(网络流(EK算法))
    图结构练习—BFSDFS—判断可达性(BFS)
    Sorting It All Out(拓扑排序)
    Power Network(最大流(EK算法))
    Labeling Balls(拓扑)
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/13997876.html
Copyright © 2020-2023  润新知