Codeforces Round # 674 (div2)

这篇博客探讨了几种算法问题,包括利用四边形性质求解第四边长度,如何优化数字矩阵使其每一行每一列形成回文串,以及解决字符串操作的复杂问题,并介绍了构建最短路径的Dijkstra算法。内容涉及数学、算法和数据结构的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A. Fence
给定 三条边的长度 让你求能组成四边的第四条边长度
其中三边不共线

方法1: 利用四边形性质 三边之和大于第四边
直接输出 a + b + c − 1 a + b + c - 1 a+b+c1 即可
方法2: 构造一个特殊的四边形
在这里插入图片描述

B. Nice Matrix

给你一个 N ∗ M N*M NM 的数字矩阵
要求每行每列都构成回文串
可以变换任意位置的数值(变化值为1)
求最小的变换次数

首先我们不处理中间行,和每列中间的元素,如果判定最后 n n n 为奇数, 再处理最中间的行, 如果 m m m 为奇数, 处理每列中间的元素

请添加图片描述
像图中一样,我们先处理红色区域的,最中间的一行先不处理,每列中最中间的先不处理

因为每列要回文,所以 a i = a m − i + 1 a_i = a_{m - i + 1} ai=ami+1
因为每行要回文,所以 a i = b i a_i = b_i ai=bi

我们可以设 a i = x 1 , a m − i + 1 = x 2 , b i = x 3 , b m − i + 1 = x 4 a_i = x_1, a_{m - i + 1} = x_2, b_i = x_3, b_{m - i + 1} = x_4 ai=x1,ami+1=x2,bi=x3,bmi+1=x4;
然后我们要使得操作数最小,我们可以给他们从小到大排序,求出中位数, 计算法每个数与中位数的差值

最后判断 n n n 是否为奇数, 如果为奇数处理一下最中间一行
m m m 是否每奇数,如果为奇数, 处理一个每行最中间的元素


#include<bits/stdc++.h>

using namespace std;

const int N =  110;
#define int long long 

int g[N][N],a[N];

void  solve()
{
    int n, m;
    cin >> n >> m;
    int res = 0;
    int x; 
    for(int i = 1; i <= n; i ++ )
    for(int j = 1; j <= m; j ++ )cin >> g[i][j];
    
    for(int i = 1; i <= n / 2; i ++ )
    for(int j = 1; j <= m / 2; j ++ )
    {
        a[1] = g[i][j], a[2] = g[i][m-j+1], a[3] = g[n-i+1][j], a[4] = g[n - i + 1 ][ m - j + 1];
        sort(a + 1, a + 5);
        int mid = (a[2] + a[3]) / 2;
        res += abs(a[1] - mid) + abs(a[2] - mid) + abs(a[3] - mid) + abs(a[4] - mid);
        
    }
    
    if(n % 2 != 0 ) 
    {
        for(int i = 1; i <= m / 2; i ++ )
        res += abs(g[n / 2 + 1][i] - g[n / 2 + 1][m - i + 1]);
    }
    if(m % 2 != 0)
    {
           for(int i = 1;i <= n / 2; i ++) 
            res+=abs(g[i][m/2 + 1] - g[n - i + 1][m / 2 + 1]);
    }
    cout << res << '\n';
}
signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while(t -- ) solve();

}

C. Bargain

题目:给出长度为n的字符串,删除其中任意长度的连续子串,将所有可能的情况的数字加起来取模

首先根据给出的数据范围,我们基本很难暴力, 所以我们想到要找规律, 于是我们先枚举一个数字来寻找规律

12345 12345 12345

当不删去 2 2 2时, 先删去 2 2 2 右边的时

当删去长度为 1 1 1 的子串时, 长度为 1 1 1 的子串有 3 3 3 个, 所以 2 2 2 对答案的贡献为 3 × 2 × 1 0 2 3 × 2 × 10^2 3×2×102
当删去长度 2 2 2 的子串时, 长度为 2 2 2 的子串有 2 2 2 个, 2 2 2 对答案的贡献为 2 × 2 × 1 0 1 2 × 2 × 10^1 2×2×101
当删去长度为 3 3 3 的子串时, 长度为 1 1 1 的子串有 1 1 1 个, 2 2 2 对答案的贡献为 1 × 2 × 1 0 0 1 × 2 × 10^0 1×2×100

不难推出
ans1 = ∑ i = 1 n \sum_{i=1}^{n} i=1n ( s [ i ] − ′ 0 ′ ) (s[i] -'0') (s[i]0) × ∑ j = 1 n − i × j × 1 0 j − 1 \sum_{j=1}^{n - i} × j × 10^{j - 1} j=1ni×j×10j1
这里 j j j 枚举的是 方案数 1 1 1 ~ n − i n-i ni

当删去 2 2 2 左边的时, 对 2 2 2 的贡献是相同的

ans2 = ∑ i = 1 n ( s [ i ] − ′ 0 ′ ) × i × ( i − 1 ) × 1 0 n − i \sum_{i=1}^{n}(s[i] - '0') × i × (i - 1) × 10^{n - i} i=1n(s[i]0)×i×(i1)×10ni

然后输出 a n s 1 + a n 2 ans1 + an2 ans1+an2 即可

#include<bits/stdc++.h>
using namespace std;

#define int long long 
const int N = 1e5 + 10, mod = 1e9 + 7;
char s[N];
int pow1[N],f[N];
signed main()
{
    scanf("%s",s + 1);
    
    int n = strlen(s + 1);   //读入
    pow1[0] = 1;    //预处理10的次方
    for(int i = 1; i <= n; i ++ ) pow1[i] = pow1[i - 1] * 10 % mod;
    int res = 0; 
    //预处理 j * 10^(j - 1)
    for(int i = 1; i <= n; i ++ ) f[i] = (f[i - 1] + (i * pow1[i - 1] % mod)) % mod; 
    for(int i = n; i >= 1; i -- )
    {
        int c = s[i] - '0'; 
         res = ( res + ((c * i * (i - 1) / 2 % mod) * pow1[n - i] % mod)) %mod;
       res=(res + f[n-i] * c % mod) % mod;
    }
     
    cout << res << '\n';
}

D. Returning Home

这道题我调了两个小时, 就一出细节,真无语死了

给出一个 n ∗ n n * n nn 的二维平面,现在要从点 ( s x , s y ) ( sx , sy ) (sx,sy) 到达点 ( f x , f y ) ( fx , fy ) (fx,fy),在平面上有 m m m 个传送门,共有两次操作:

  1. 向上、下、左、右移动一个单位,花费为一个单位
  2. 可以瞬间移动到同行或同列的传送门,没有花费

现在问从起点到终点最少需要多少时间

不难发现这是个最短路问题, 这道题最关键的是建图, 建完图后直接跑 D i j k s t r a Dijkstra Dijkstra 就可以了

建图, 他给出了一系列点, 只要当到达同一行,同一列时,可以直接传送到这个点上,所以我们就先计算出从起点到他同行或者同列最小的花费,然后建边
a d d ( 0 , i , m i n ( a b s ( x i − s x ) , a b s ( y i − s y ) add(0,i, min(abs(x_i - sx), abs(y_i - sy) add(0,i,min(abs(xisx),abs(yisy)

其次建立从该点到达终点的费用,然后建边
a d d ( i , m + 1 , a b s ( x i − f x ) + a b s ( y i − f y ) ) add(i, m + 1, abs(x_i - fx) + abs(y_i - fy)) add(i,m+1,abs(xifx)+abs(yify))

然后为了保证能够到达终点,建立一条从起点到终点的边
a d d ( 0 , m + 1 , a b s ( s x − f x ) + a b s ( s y − f y ) ) add(0, m + 1, abs(sx - fx) + abs(sy - fy)) add(0,m+1,abs(sxfx)+abs(syfy))

然后这些点,点与点之间也可以建边, 为了保证花费最小, 我们分别按 x x x 排序, 和按 y y y 排序, 然后建边
x x x 排序
a d d ( a [ i ] . i d x , a [ i + 1 ] . i d x , a [ i + 1 ] . x − a [ i ] . x ) add(a[i].idx, a[i + 1].idx, a[i + 1].x - a[i].x) add(a[i].idx,a[i+1].idx,a[i+1].xa[i].x)

y y y 排序
a d d ( a [ i ] . i d x , a [ i + 1 ] . i d x , a [ i + 1 ] . y − a [ i ] . y ) add(a[i].idx, a[i + 1].idx, a[i + 1].y - a[i].y) add(a[i].idx,a[i+1].idx,a[i+1].ya[i].y)

最后, 我们记得要建两条边

然后跑 D i j k s t r a Dijkstra Dijkstra 即可, 代码如下

#include<bits/stdc++.h>

using namespace std;

#define int long long 
const int N = 1e6 + 10;

typedef pair<int, int> PII;

int e[N], ne[N], w[N], h[N], idx, n, m, sx, sy, fx, fy, dist[N];

bool st[N]; 
struct node
{
    int x, y, idx;
}a[N];

bool cmp1(node a, node b)
{
    return a.x < b.x;
}

bool cmp2(node a, node b)
{
    return a.y < b.y;
}

void add(int a, int b, int c)  //建双边
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
    e[idx] = a, ne[idx] = h[b], w[idx] = c, h[b] = idx ++;
}

int  dijkstra()   //上板子
{
    memset(dist, 0x7f, sizeof dist);
    dist[0] = 0;
    
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    
    heap.push({0, 0});
    
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int ver = t.second;
        if(st[ver]) continue;
        st[ver] = true;
        
        for(int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j], j});
            }
        }
    }
    return dist[m + 1];
}
signed main()
{
    cin >> n >> m;
    cin >> sx >> sy >> fx >> fy;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i ++ )   //建边
    {
        cin >> a[i].x >> a[i].y;
        a[i].idx = i;
        add(0, i, min(abs(a[i].x - sx), abs(a[i].y - sy)));
        add(i, m + 1, abs(a[i].x - fx) + abs(a[i].y - fy));
    }
    
    add(0, m + 1, abs(sx - fx) + abs(sy - fy));  //建边
    
    sort(a + 1, a + 1 + m, cmp1);   //排序
    for(int i = 1; i < m; i ++ )   //建边
        add(a[i].idx, a[i + 1].idx, a[i + 1].x - a[i].x);
    sort(a + 1, a + 1 + m, cmp2);  //排序
    for(int i = 1; i < m; i ++ )  //建边
    add(a[i].idx, a[i + 1].idx , a[i + 1].y - a[i].y);
    
    cout << dijkstra();
    
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广西小蒟蒻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值