A. Fence
给定 三条边的长度 让你求能组成四边的第四条边长度
其中三边不共线
方法1: 利用四边形性质 三边之和大于第四边
直接输出
a
+
b
+
c
−
1
a + b + c - 1
a+b+c−1 即可
方法2: 构造一个特殊的四边形
B. Nice Matrix
给你一个
N
∗
M
N*M
N∗M 的数字矩阵
要求每行每列都构成回文串
可以变换任意位置的数值(变化值为1)
求最小的变换次数
首先我们不处理中间行,和每列中间的元素,如果判定最后 n n n 为奇数, 再处理最中间的行, 如果 m m m 为奇数, 处理每列中间的元素
像图中一样,我们先处理红色区域的,最中间的一行先不处理,每列中最中间的先不处理
因为每列要回文,所以
a
i
=
a
m
−
i
+
1
a_i = a_{m - i + 1}
ai=am−i+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,am−i+1=x2,bi=x3,bm−i+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=1n−i×j×10j−1
这里
j
j
j 枚举的是 方案数
1
1
1 ~
n
−
i
n-i
n−i
当删去 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×(i−1)×10n−i
然后输出 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 n∗n 的二维平面,现在要从点 ( s x , s y ) ( sx , sy ) (sx,sy) 到达点 ( f x , f y ) ( fx , fy ) (fx,fy),在平面上有 m m m 个传送门,共有两次操作:
- 向上、下、左、右移动一个单位,花费为一个单位
- 可以瞬间移动到同行或同列的传送门,没有花费
现在问从起点到终点最少需要多少时间
不难发现这是个最短路问题, 这道题最关键的是建图, 建完图后直接跑 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(xi−sx),abs(yi−sy)
其次建立从该点到达终点的费用,然后建边
即
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(xi−fx)+abs(yi−fy))
然后为了保证能够到达终点,建立一条从起点到终点的边
即
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(sx−fx)+abs(sy−fy))
然后这些点,点与点之间也可以建边, 为了保证花费最小, 我们分别按
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].x−a[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].y−a[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();
}