Codeforces Round #677 (Div. 3)

Posted dyhaohaoxuexi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

















以上是关于Codeforces Round #677 (Div. 3)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #677 (Div. 3)

Codeforces Round #677 (Div. 3)

Codeforces Round #677 (Div. 3)D. Districts Connection

Codeforces Round #436 E. Fire(背包dp+输出路径)

[ACM]Codeforces Round #534 (Div. 2)

codeforces 677A-C语言解题报告