目录
A - A Hard Problem(找规律)
思路
打表之后,可以得出一个答案的通项公式 ( n + 1 ) / 2 + 1 (n+1)/2+1 (n+1)/2+1
代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t; cin >> t;
while(t --)
{
int n; scanf("%d", &n);
printf("%d\n", (n + 1) / 2 + 1);
}
return 0;
}
C - Digital Path(dp、记忆化搜索)
思路
显然是记忆化搜索的题(经典滑雪),很容易定义
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示以位置
(
i
,
j
)
(i, j)
(i,j)结尾的最长长度为
k
k
k的路径数量。但是会爆空间,发现当路径长度大于3之后,路径长度是多少对答案并没有影响,因此可以统一到一起。换一下
k
k
k的定义,当
k
=
1
,
2
,
3
k=1,2,3
k=1,2,3,
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]就表示以位置
(
i
,
j
)
(i, j)
(i,j)结尾的最长长度为
k
k
k的路径数量;当
k
=
4
k=4
k=4,
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]就表示以位置
(
i
,
j
)
(i, j)
(i,j)结尾的最长长度大于等于
k
k
k的路径数量;
d
p
[
i
]
[
j
]
[
k
]
=
∑
d
p
[
n
i
]
[
n
j
]
[
k
−
1
]
,
2
<
=
k
<
=
4
;
d
p
[
i
]
[
j
]
[
4
]
=
∑
d
p
[
n
i
]
[
n
j
]
[
4
]
dp[i][j][k] = \sum dp[ni][nj][k-1], 2<= k <= 4;\ dp[i][j][4] = \sum dp[ni][nj][4]
dp[i][j][k]=∑dp[ni][nj][k−1],2<=k<=4; dp[i][j][4]=∑dp[ni][nj][4]其中
(
n
i
,
n
j
)
(ni, nj)
(ni,nj)是
(
i
,
j
)
(i,j)
(i,j)的相邻位置且
a
[
n
i
]
[
n
j
]
=
a
[
i
]
[
j
]
−
1
a[ni][nj] = a[i][j] - 1
a[ni][nj]=a[i][j]−1。
然后还需要考虑特殊情况:当
d
p
[
i
]
[
j
]
[
2
/
3
/
4
]
dp[i][j][2/3/4]
dp[i][j][2/3/4]任意一个不为0,需要将
d
p
[
i
]
[
j
]
[
1
]
dp[i][j][1]
dp[i][j][1]设置为0,因为到位置
(
i
,
j
)
(i,j)
(i,j)的最长路必然不可能是长度1的了。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
int a[N][N];
int dp[N][N][5];
int n, m;
bool is_max(int x, int y)
{
for(int i = 0; i < 4; i ++)
{
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
if(a[nx][ny] == a[x][y] + 1) return false;
}
return true;
}
void dfs(int x, int y)
{
if(dp[x][y][1] != -1) return;
dp[x][y][1] = 1;
dp[x][y][2] = dp[x][y][3] = dp[x][y][4] = 0;
for(int i = 0; i < 4; i ++)
{
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
if(a[nx][ny] == a[x][y] - 1)
{
dfs(nx, ny);
if(dp[nx][ny][4] > 0) dp[x][y][4] = (dp[x][y][4] + dp[nx][ny][4]) % mod;
for(int j = 4; j >= 2; j --)
{
if(dp[nx][ny][j - 1] > 0)
dp[x][y][j] = (dp[x][y][j] + dp[nx][ny][j - 1]) % mod;
}
}
}
if (dp[x][y][2] > 0 || dp[x][y][3] > 0 || dp[x][y][4] > 0) dp[x][y][1] = 0;
// for(int i = 1; i <= 4; i ++) cout << x << ' ' << y << ' ' << a[x][y] << ' ' << i << ' ' << ' ' << dp[x][y][i] << endl;
}
int main()
{
memset(dp, -1, sizeof dp);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) scanf("%d", &a[i][j]);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
if(dp[i][j][1] == -1) dfs(i, j);
int ans = 0;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= m; j ++)
{
if(is_max(i, j) && dp[i][j][4] > 0) ans = (ans + dp[i][j][4]) % mod;
}
}
printf("%d\n", ans);
return 0;
}
H - Prince and Princess(思维)
思路
题意很迷惑,没有说王子知不知道每种人物的数量。
于是假设如果王子不知道,那么只要存在说假话与真假不定的人,那么就会输出NO,那这题就没意义了。所以王子知道每种人的数量。
然后呢,考虑以下
- 三个问题王子只选“公主在哪个房间?”这个问题去问,其他的问题没有什么意义
- 考虑说真话的人数 a a a多于【说假话和随便说的人】的和 b + c b + c b+c,那么王子问完每个人“公主在哪个房间?”至少会有 a a a个人说的房间是相同的,相同房间的数量也可能多于 a a a,因为 c c c个随便说的人也可能说是真正的那个房间。
- 再考虑特殊情况: a = 10 , b = 1 , c = 1 a=10, b=1, c=1 a=10,b=1,c=1,其实不用询问所有的人,而只需要询问5次就可获得至少3次相同的房间,显然说出3个相同房间的人必然都是说真话的人,因为说假话和随便说的人都没3个人。因此当 a > b + c a>b+c a>b+c,只需要询问 2 ( b + c ) + 1 2(b+c)+1 2(b+c)+1次就可获得真正的房间。
- 最后的极端情况:只有一个房间,一次询问都不需要,公主必然在该房间内。
代码
#include <iostream>
using namespace std;
int main()
{
int a, b, c;
cin >> a >> b >> c;
int d = (b + c) * 2 + 1;
if (a == 1 && b == 0 && c == 0)
{
cout << "YES\n0\n";
return 0;
}
if (a + b + c < d) cout << "NO" << endl;
else cout << "YES\n" << d << endl;
return 0;
}
J - Spy(期望、二分图最大权匹配)
思路
题意比较绕,大概是这样,给你大小为
n
n
n的四个数组,
a
,
p
,
b
,
c
a,p,b,c
a,p,b,c,你可以将
b
,
c
b,c
b,c任意两两匹配,匹配完加起来成为数组
d
d
d,然后将
a
a
a与
d
d
d随机匹配,如果
a
[
i
]
<
d
[
i
]
a[i] < d[i]
a[i]<d[i],则会获得
p
[
i
]
p[i]
p[i]的金币,把所有随机情况都求一遍获得的金币总数,可以得到一个金币总数期望。现在让你找到一个
b
,
c
b,c
b,c匹配,使得该匹配获得的金币总数期望最大,输出该期望*n作为答案。
由于是两两匹配,往二分图匹配的方向考虑。考虑
b
i
b_i
bi与
c
j
c_j
cj匹配之后会对期望贡献多少,设
d
=
b
i
+
c
j
d=b_i+c_j
d=bi+cj,则在与
a
a
a数组的所有匹配情况中,
d
d
d与
a
k
a_k
ak匹配到的概率为
(
n
−
1
)
!
n
!
=
1
n
\frac{(n-1)!}{n!}=\frac{1}{n}
n!(n−1)!=n1,贡献的期望为
1
n
∗
p
k
[
d
>
a
k
]
\frac{1}{n}*p_k[d > a_k]
n1∗pk[d>ak]。又因为最后算出的期望需要乘上n,所以贡献的期望为
p
k
[
d
>
a
k
]
p_k[d>a_k]
pk[d>ak]。
那么对于每一对
b
i
b_i
bi和
c
j
c_j
cj,我们枚举
a
k
a_k
ak,然后将
b
i
+
c
j
>
a
k
b_i + c_j > a_k
bi+cj>ak的
p
k
p_k
pk加到
b
i
b_i
bi与
c
j
c_j
cj的边权上即可。
最后用KM做一遍二分图最大匹配就可以啦!
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 4E2 + 10;
int w[N][N];
int la[N], lb[N];
bool va[N], vb[N];
int mat[N];
int n, delta, upd[N], last[N];
LL a[N], b[N], c[N];
int p[N];
bool dfs(int x, int fa)
{
va[x] = 1;
for(int y = 1; y <= n; y ++)
if(!vb[y])
{
if(la[x] + lb[y] - w[x][y] == 0)
{
vb[y] = 1,last[y] = fa;
if(!mat[y] || dfs(mat[y], y))
{
mat[y] = x;
return true;
}
}
else if(upd[y] > la[x] + lb[y] - w[x][y])
{
upd[y] = la[x] + lb[y] - w[x][y];
last[y] = fa;
}
}
return false;
}
LL km()
{
for(int i = 1; i<= n; i ++)
{
la[i] = -(1 << 30); lb[i] = 0;
for(int j = 1; j <= n; j ++)
la[i] = max(la[i], w[i][j]);
}
for(int i = 1; i <= n; i ++)
{
memset(va, 0, sizeof va), memset(vb, 0, sizeof vb);
memset(last, 0, sizeof last), memset(upd, 0x7f, sizeof upd);
int st = 0; mat[0] = i;
while(mat[st])
{
LL delta = 1ll << 60;
if(dfs(mat[st], st)) break;
for(int j = 1; j <= n; j ++)
if(!vb[j] && upd[j] < delta)
{
delta = upd[j];
st = j;
}
for(int j = 1; j <= n; j ++)
{
if(va[j]) la[j] -= delta;
if(vb[j]) lb[j] += delta;
else upd[j] -= delta;
}
vb[st] = 1;
}
while(st)
{
mat[st] = mat[last[st]];
st = last[st];
}
}
LL ans = 0;
for(int i = 1; i <= n; i ++) ans += w[mat[i]][i];
return ans;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) cin >> p[i];
for(int i = 1; i <= n; i ++) cin >> b[i];
for(int i = 1; i <= n; i ++) cin >> c[i];
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
for(int k = 1; k <= n; k ++)
if(a[k] < b[i] + c[j]) w[i][j] += p[k];
}
cout << km() << endl;
return 0;
}
K - Triangle(简单计算几何)
思路
(一开始没用计算几何板子wa🤮了,之后用了个板子就过了)
首先,判断一下点是否在三角形边上,不是的话输出-1。
然后,我特判了点在三角形顶点与三角形某一边中点的情况。
最后,直接分类讨论,把点在三角形六个区域的情况都考虑一下即可。比如下面这种情况:
假设
P
P
P为题目给定的点(在
B
C
BC
BC中点右边),那么所求的点必然在
A
B
AB
AB上(假设为
D
D
D)。那么有:
1
2
S
A
B
C
=
S
B
D
P
\frac{1}{2}S_{ABC}=S_{BDP}
21SABC=SBDP
故:
1
2
1
2
A
B
∗
B
C
∗
s
i
n
B
=
1
2
B
D
∗
B
P
∗
s
i
n
B
\frac{1}{2}\frac{1}{2}AB*BC*sinB=\frac{1}{2}BD*BP*sinB
2121AB∗BC∗sinB=21BD∗BP∗sinB
于是:
B
D
A
B
=
1
2
B
C
B
P
\frac{BD}{AB} = \frac{1}{2}\frac{BC}{BP}
ABBD=21BPBC
于是可以求出
B
D
BD
BD与
A
B
AB
AB的比值,于是就可以利用
A
B
AB
AB向量求出
B
D
BD
BD向量进而求出
D
D
D点。
其他的情况也是类似滴。
(代码很丑建议别看)
代码
#include <iostream>
#include <cmath>
using namespace std;
const double eps = 1e-8;
int sgn(double x)
{
if (fabs(x) < eps) return 0;
else return x < 0 ? -1 : 1;
}
struct Point
{
double x, y;
Point(){}
Point(double x, double y):x(x), y(y){}
Point operator - (const Point& b) const
{
return Point(x - b.x, y - b.y);
}
}p[5];
struct Line
{
Point p1, p2;
Line(){}
Line(Point p1, Point p2):p1(p1), p2(p2){}
}l[5];
double Dist(Point a, Point b)
{
double dx = a.x - b.x, dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
double Dot(Point a, Point b)
{
return a.x * b.x + a.y * b.y;
}
double Cross(Point a, Point b)
{
return a.x * b.y - a.y * b.x;
}
bool Point_on_Line(Point P, Line L)
{
return sgn(Cross(P - L.p1, L.p2 - L.p1)) == 0 && sgn(Dot(P - L.p1, P - L.p2)) <= 0;
}
double getmid(double a, double b)
{
return (a + b) / 2;
}
struct Node
{
Point p1, p2;
double d;
};
Node solve()
{
Node res;
if (Point_on_Line(p[4], l[1]))
{
if (Dist(p[1], p[4]) < Dist(p[2], p[4]))
{
res.p1 = p[2], res.p2 = p[3];
res.d = Dist(p[1], p[2]) / Dist(p[4], p[2]) / 2;
}
else
{
res.p1 = p[1], res.p2 = p[3];
res.d = Dist(p[1], p[2]) / Dist(p[4], p[1]) / 2;
}
}
else if(Point_on_Line(p[4], l[3]))
{
if (Dist(p[3], p[4]) < Dist(p[1], p[4]))
{
res.p1 = p[1], res.p2 = p[2];
res.d = Dist(p[1], p[3]) / Dist(p[4], p[1]) / 2;
}
else
{
res.p1 = p[3], res.p2 = p[2];
res.d = Dist(p[1], p[3]) / Dist(p[4], p[3]) / 2;
}
}
else
{
if (Dist(p[2], p[4]) < Dist(p[3], p[4]))
{
res.p1 = p[3], res.p2 = p[1];
res.d = Dist(p[2], p[3]) / Dist(p[4], p[3]) / 2;
}
else
{
res.p1 = p[2], res.p2 = p[1];
res.d = Dist(p[2], p[3]) / Dist(p[4], p[2]) / 2;
}
}
return res;
}
int main()
{
int _;
scanf("%d", &_);
while (_ --)
{
for (int i = 1; i <= 4; i ++ )
scanf("%lf%lf", &p[i].x, &p[i].y);
if (p[1].x == p[4].x && p[1].y == p[4].y)
{
printf("%.10f %.10f\n", getmid(p[2].x, p[3].x), getmid(p[2].y, p[3].y));
continue;
}
if (p[2].x == p[4].x && p[2].y == p[4].y)
{
printf("%.10f %.10f\n", getmid(p[1].x, p[3].x), getmid(p[1].y, p[3].y));
continue;
}
if (p[3].x == p[4].x && p[3].y == p[4].y)
{
printf("%.10f %.10f\n", getmid(p[1].x, p[2].x), getmid(p[1].y, p[2].y));
continue;
}
l[1] = Line(p[1], p[2]);
l[2] = Line(p[2], p[3]);
l[3] = Line(p[3], p[1]);
if (!Point_on_Line(p[4], l[1]) && !Point_on_Line(p[4], l[2]) && !Point_on_Line(p[4], l[3]))
{
puts("-1");
continue;
}
if (Point_on_Line(p[4], l[1]) && p[4].x == getmid(p[1].x, p[2].x) && p[4].y == getmid(p[1].y, p[2].y))
{
printf("%.10f %.10f\n", p[3].x, p[3].y);
continue;
}
if (Point_on_Line(p[4], l[2]) && p[4].x == getmid(p[3].x, p[2].x) && p[4].y == getmid(p[3].y, p[2].y))
{
printf("%.10f %.10f\n", p[1].x, p[1].y);
continue;
}
if (Point_on_Line(p[4], l[3]) && p[4].x == getmid(p[1].x, p[3].x) && p[4].y == getmid(p[1].y, p[3].y))
{
printf("%.10f %.10f\n", p[2].x, p[2].y);
continue;
}
Node res = solve();
Point p1 = res.p1, p2 = res.p2;
double a = res.d;
printf("%.10f %.10f\n", a * (p2 - p1).x + p1.x, a * (p2 - p1).y + p1.y);
}
return 0;
}