Codeforces Round #677 (Div. 3)

Codeforces Round #677 (Div. 3)

[Link](Dashboard - Codeforces Round #677 (Div. 3) - Codeforces)

A. Boring Apartments

思路

  • 模拟

最后一个之前每一组数都是按了十次,最后一个按题意模拟即可

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
    
        int res = (n % 10 - 1) * 10;
        int cnt = 1;
        while (n) {
            res += cnt;
            cnt ++;
            n /= 10;
        }
        cout << res << '\n';
    }
    return 0;
}

B. Yet Another Bookshelf

思路

  • 贪心

​ 最左端和最右端的 1 1 1中间的 0 0 0是一定要消失的,其余的均可不管,因此答案就是中间 0 0 0的数量

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];
        int st, ed;
        for (int i = 1; i <= n; i ++)
            if (a[i]) {
                st = i;
                break;
            }
        for (int i = n; i; i --) 
            if (a[i]) {
                ed = i;
                break;
            }

        int cnt = 0;
        for (int i = st; i <= ed; i ++)
            if (!a[i])
                cnt ++;
        
        cout << cnt << '\n';
    }
    return 0;
}

C. Dominant Piranha

思路

  • 思维

    如果全是一样的则无解,否则贪心来看找最大的,由于不全都一样,因此一定存在一个位置是最大的且它的左右不都是最大的,从这个点吃即可

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 3e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        int mx = -1;
        for (int i = 1; i <= n; i ++) cin >> a[i], mx = max(a[i], mx);
        int res = -1;
        a[0] = a[n + 1] = mx;
        for (int i = 1; i <= n; i ++) 
            if (a[i] == mx && (a[i + 1] < mx || a[i - 1] < mx)) {
                res = i;
                break;
            }
        cout << res << '\n';
    }
    return 0;
}

D. Districts Connection

思路

  • 构造

​ 如果都属于一个帮派则一定无解,否则一定至少存在两个帮派,因此我们一个帮派的某个城镇为根,所有不是这个帮派的城镇与之相连,这个帮派剩下的城镇随便和连着它的某个帮派相连即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
PII a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i].x, a[i].y = i;
        sort(a + 1, a + 1 + n);
        int s = 1;
        while (s <= n && a[s].x == a[1].x) s ++;      
        if (s == n + 1)  {
            cout << "NO\n";
            continue ;
        }

        vector<PII> res;
        int pre = a[1].y;
        
        for (int i = s; i <= n; i ++) {
            int j = i;
            while (j <= n && a[j].x == a[i].x) j ++;
            j --;            
            for (int k = i; k <= j; k ++)
                res.push_back({pre, a[k].y});
            i = j;
            pre = a[i].y;
        }

        for (int i = 2; i <= s - 1; i ++) 
            res.push_back({pre, a[i].y});
        cout << "YES\n";

        for (auto t : res) 
            cout << t.x << ' ' << t.y << '\n';

    }
    return 0;
}

E. Two Round Dances

思路

  • 组合数学

​ 对于一个长度为 n n n的圆排列有 ( n − 1 ) ! (n-1)! (n1)!种情况,因为长度为 n n n的序列的排列有 n ! n! n!种类,对于长度为 n n n的某个圆排列任减一个边会构成 n n n个排列,因此圆排列为 ( n − 1 ) ! (n-1)! (n1)!种情况。

​ 从 n n n个数里取出 n / 2 n/2 n/2个为 C n n / 2 C_n^{n/2} Cnn/2,取出 n / 2 n/2 n/2个以后剩下的也就确定了,因此总取法要 / 2 /2 /2,对于每种取法,取出的数构成圆排列,留下的也构成圆排列因此符合乘法原理,直接计数即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
// (n, 2n) / n
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n;
    LL res = 1;       
    for (int i = n; i > n / 2; i --) res *= i;    
    for (int i = 1; i <= n / 2; i ++) res /= i;
    res /= 2;
    for (int i = 1; i < n / 2; i ++) res *= i;
    for (int i = 1; i < n / 2; i ++) res *= i;
    cout << res << '\n';
    return 0;
}

F. Zero Remainder Sum

思路

  • d p dp dp

很显然有局部最优解,因此考虑设 f [ i ] [ j ] [ k ] : 前 i 行 且 第 i 行 选 了 j 个 且 和 模 为 k 的 最 大 值 f[i][j][k]:前i行且第i行选了j个且和模为k的最大值 f[i][j][k]:iijk,考虑当前这个状态从哪个状态转移过来,分为两种同层之间的转移和相邻层之间的转移,首先同层之间对于每个元素选或不选两种可能,不选的情况直接从前一维复制过来因此只需要考虑选的情况选当前这个可以从前面哪个转移过过来,对于不同层的来看,则从前一层转移过来的时候不受 j j j这一维的限制,相当于都从 f [ i − 1 ] [ m / 2 ] [ k ] f[i-1][m/2][k] f[i1][m/2][k]转移过来,因此决策当前这个选不选再转移即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, p;
int a[71][71];
LL f[71 * 71][40][70];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> m >> p;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            cin >> a[i][j];
    memset(f, -0x3f, sizeof f);
    int g = 0;
    f[0][0][0] = 0;
    for (int j = 1; j <= n; j ++)
        for (int i = 1; i <= m; i ++) {
            g ++;
            if (i > 1) {
                for (int kk = 0; kk <= min(m / 2, i); kk ++) {
                    for (int k = 0; k < p; k ++) {                        
                        f[g & 1][kk][k] = max(f[g & 1][kk][k], f[g - 1 & 1][kk][k]);    
                        if (kk)
                            f[g & 1][kk][(a[j][i] + k) % p] = max(f[g - 1 & 1][kk][(a[j][i] + k) % p], f[g - 1 & 1][kk - 1][k] + a[j][i]);                            
                    }                    
                }
            }
            else {
                for (int kk = 0; kk <= m / 2; kk ++) 
                    for (int k = 0; k < p; k ++) {
                        f[g & 1][0][k] = max(f[g & 1][0][k], f[g - 1 & 1][kk][k]);
                        f[g & 1][1][(a[j][i] + k) % p] = max(f[g & 1][1][(a[j][i] + k) % p], f[g - 1 & 1][kk][k] + a[j][i]);
                    }
            }
           
            
            
        }

    LL res = 0;
    
    for (int i = 0; i <= m / 2; i ++) res = max(res, f[g & 1][i][0]);
    cout << res << '\n';
    return 0;
}

G. Reducing Delivery Cost

思路

  • 最短路,枚举

考虑我们让一条边 ( a , b ) (a,b) (a,b)边权为 0 0 0会产生什么影响,对于两个点 u , v u,v u,v来说,它们之间的最短距离会出现三种情况 d i s t [ u ] [ v ] , d i s t [ u ] [ a ] + d i s t [ b ] [ v ] , d i s t [ u ] [ b ] + d i s t [ a ] [ v ] dist[u][v],dist[u][a] + dist[b][v],dist[u][b] + dist[a][v] dist[u][v],dist[u][a]+dist[b][v],dist[u][b]+dist[a][v],因此考虑暴力枚举删除哪个点对然后对每个需要算贡献的点对加一下最优情况即可

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int d[1010][1010];

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++) 
            if (i != j) d[i][j] = INF;
    
    vector<PII> ve;
    while (m --) {
        int x, y, z; cin >> x >> y >> z; 
        ve.push_back({x, y});
        d[x][y] = d[y][x] = min(d[x][y], z);
    }

    vector<PII> tag;
    for (int i = 1; i <= k; i ++) {
        int x, y; cin >> x >> y;
        tag.push_back({x, y});
    }

    for (int kk = 1; kk <= n; kk ++)
        for (int i = 1; i <= n; i ++)
            for (int j = 1; j <= n; j ++)
                d[i][j] = min(d[i][j], d[i][kk] + d[kk][j]);
    

    LL res = 0; 
    for (auto t : tag)  res += d[t.x][t.y];

    for (int i = 0; i < ve.size(); i ++) {
        LL v = 0;
        for (auto t : tag) 
            v += min(d[t.x][t.y], min(d[t.x][ve[i].x] + d[ve[i].y][t.y], d[t.x][ve[i].y] + d[ve[i].x][t.y]));

        // }
        res = min(res, v);
    }

    cout << res << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值