题意 给定 m m m 个点的坐标 ( x i , y i ) (x_i,y_i) (xi,yi),依次相连,台风在这些线段上移动,给定 n n n 个庇护所的坐标 ( X i , Y i ) (X_i,Y_i) (Xi,Yi),求对每个庇护所而言,台风的影响半径最大为多大时,该庇护所恰好被影响到
Tag 计算几何
题解 先计算 c o s < A B → , A P → > cos<\overrightarrow{AB},\overrightarrow{AP}> cos<AB,AP> 和 c o s < B A → , B P → > cos<\overrightarrow{BA},\overrightarrow{BP}> cos<BA,BP> 是否为负,若为负则 P P P 到线段 A B AB AB 的垂足不在线段 A B AB AB 上,则取 m i n ( ∣ P A → ∣ , ∣ P B → ) min(|\overrightarrow{PA}|,|\overrightarrow{PB}) min(∣PA∣,∣PB) 即可,否则利用叉乘算 P P P 到线段 A B AB AB 的高度(本质上是算面积),最后取 m i n min min 即可
#include
#include
#include
#include
using namespace std;
struct Point { double x, y; };
Point operator+ (Point A, Point B) { return {A.x + B.x, A.y + B.y}; }
Point operator- (Point A, Point B) { return {A.x - B.x, A.y - B.y}; }
double operator* (Point A, Point B) { return A.x * B.x + A.y * B.y; }
double operator^ (Point A, Point B) { return A.x * B.y - A.y * B.x; }
double len(Point P) { return sqrt(pow(P.x, 2) + pow(P.y, 2)); }
double dot(Point P, Point A, Point B) { return (P - A) * (P - B); } // 点乘
double cross(Point A, Point B, Point C) { return (A - B) ^ (A - C); } // 叉乘
vector<Point> typhoon, shelter;
int main() {
int n, m; cin >> m >> n;
typhoon.resize(m), shelter.resize(n);
for (auto &[x, y] : typhoon) cin >> x >> y;
for (auto &[x, y] : shelter) cin >> x >> y;
for (auto P : shelter) {
double res = 2e9;
for (int i = 1; i < m; i ++) {
Point A = typhoon[i - 1], B = typhoon[i];
if (dot(A, P, B) <= 0 || dot(B, P, A) <= 0) {
res = min(res, min(len(A - P), len(B - P)));
} else {
res = min(res, fabs(cross(P, A, B) / len(A - B)));
}
}
cout << fixed << setprecision(4) << res << endl;
}
return 0;
}
题意 n n n 个红绿蓝球,按顺序操作,给定的字符串包含 R G B RGB RGB 一一对应,有两个袋子,有袋子 1 1 1 和 2 2 2,每个袋子一次只能装一个球,可以直接扔掉,优先装袋子 1 1 1,每次拿一个球,若两个袋子都满了,有以下三种情况
Tag dp
题解 开始初始化初始所有状态情况为 − i n f -inf −inf,令初始状态 f [ 0 ] [ 0 ] = 0 f[0][0]=0 f[0][0]=0,考虑如下情况,最后取考虑了前 n n n 个球的情况的所有最终状态的得分,取 m a x max max 即可
状态表示 f [ i ] [ g e t ( a , b ) ] f[i][get(a,b)] f[i][get(a,b)] 表示前 i i i 个球、袋子 1 1 1 为 a a a 颜色的球、袋子 2 2 2 为 b b b 颜色的球的最大得分
g e t ( a , b ) = ( a < < 2 ∣ b ) = a × 4 + b get(a,b)=(a<<2|b)=a×4+b get(a,b)=(a<<2∣b)=a×4+b 是对 a a a 和 b b b 状态表示, a a a 和 b b b 均有 0 − 3 0-3 0−3 四种状态,一共是 16 16 16 种
状态计算
#include
#include
#define get(a, b) (a << 2 | b)
#define endl '\n'
using namespace std;
const int N = 1e5 + 2, M = 16;
int f[N][M];
int solve() {
string s; cin >> s;
int n = s.size();
for (int i = 1; i <= n; i ++) {
int c = s[i - 1] == 'R' ? 1 : (s[i - 1] == 'G' ? 2 : 3);
for (int j = 0; j < M; j ++) f[i][j] = f[i - 1][j]; // 直接扔掉球
for (int a = 0; a <= 3; a ++)
for (int b = 0; b <= 3; b ++)
if (a == b && b == c)
for (int d = 1; d <= 3; d ++) f[i][d] = max(f[i][d], f[i - 1][get(a, b)] + 1);
// d 放在 2 号袋子是因为下一轮会发现袋子 2 空着, 会执行第三种, (0, d = b) 会变成 (b, c)
// 如果 d 放在袋子 1 里面,下一轮会直接把该球扔掉(而此时俩袋子并未同时装满,不符题意)
else if (a && b && a != b && a != c && b != c)
for (int d = 1; d <= 3; d ++)
for (int e = 1; e <= 3; e ++)
f[i][get(d, e)] = max(f[i][get(d, e)], f[i - 1][get(a, b)]);
else
f[i][get(b, c)] = max(f[i][get(b, c)], f[i - 1][get(a, b)]);
}
int res = 0;
for (int i = 0; i < M; i ++) res = max(res, f[n][i]);
return res;
}
int main() {
memset(f[0], 192, sizeof(f[0])); // 初始化为 -inf,表示无法到达这种状态
f[0][0] = 0; // 起始状态得分为 0
int T; cin >> T;
while (T --) cout << solve() << endl;
return 0;
}
法二:贪心, t = 0 , 1 , 2 t=0,1,2 t=0,1,2 表示从上轮的结果转移过来,这轮要先自选 t t t 个球
#include
using namespace std;
int solve() {
string s; cin >> s;
int n = s.size(), r = 0, g = 0, b = 0, res = 0, t = 0;
for (int i = 0; i < n; ) {
if (t == 1) {
if (i == n - 1) break; // 只剩两个球,不能得分了
if (s[i] == s[i + 1]) t = 1, res ++;
else t = 2;
i += 2; // 跳过本次和下一次
} else if (t == 2) {
t = 1, res ++, i ++; // 跳过本次
} else {
if (s[i] == 'R') r ++;
else if (s[i] == 'G') g ++;
else b ++;
if (r && g && b) t = 2;
else if (r == 3 || g == 3 || b == 3) t = 1, res ++;
i ++; // 继续
}
}
return res;
}
int main() {
int T; cin >> T;
while (T --) cout << solve() << endl;
return 0;
}
题意 有 k k k 次机会,每次赢的概率为 a b \frac a b ba,第 k k k 次赢得 k m k^m km 分,求总分的期望
Tag 逆元
快速幂
组合数学
题解
期望 = ∑ i = 1 n C n i a i ( b − a ) n − i b n ∑ j = 1 i j m = a b − n ( b − a ) n − 1 ∑ i = 1 n C n i [ a ( b − a ) − 1 ] i − 1 ∑ j = 1 i j m 期望=\sum_{i=1}^{n}C_n^i\frac{a^i(b-a)^{n-i}}{b^n}\sum_{j=1}^{i}j^m =ab^{-n}(b-a)^{n-1}\sum_{i=1}^{n}C_n^{i}[a(b-a)^{-1}]^{i-1}\sum_{j=1}^{i}j^m 期望=∑i=1nCnibnai(b−a)n−i∑j=1ijm=ab−n(b−a)n−1∑i=1nCni[a(b−a)−1]i−1∑j=1ijm
组合数部分 A A = C n i AA=C_n^i AA=Cni
概率部分 B B = a ( b − a ) n − 1 [ a ( b − a ) − 1 ] i − 1 BB=a(b-a)^{n-1}[a(b-a)^{-1}]^{i-1} BB=a(b−a)n−1[a(b−a)−1]i−1
得分部分 C C = ∑ j = 1 i j m CC=\sum_{j=1}^{i}j^m CC=∑j=1ijm
答案 r e s = b − n ∑ i = 1 n A A × B B × C C ( m o d p ) res=b^{-n}\sum_{i=1}^{n}AA×BB×CC\pmod p res=b−n∑i=1nAA×BB×CC(modp)
#include
#define endl '\n'
using namespace std;
typedef long long ll;
const ll mod = 998244353;
ll n, m, a, b;
inline ll qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) (res *= a) %= mod;
(a *= a) %= mod;
b >>= 1;
}
return res;
}
inline ll inv(ll x) {
return qpow(x, mod - 2);
}
int solve() {
cin >> n >> m >> a >> b;
ll res = 0;
ll k = a * inv(b - a) % mod;
ll AA = n % mod, BB = qpow(b - a, n - 1) * a % mod, CC = 1;
for (ll i = 1; i <= n; i ++) {
(res += AA * BB % mod * CC % mod) %= mod;
AA = AA * (n - i) % mod * inv(i + 1) % mod;
(BB *= k) %= mod;
(CC += qpow(i + 1, m)) %= mod;
}
return (res * inv(qpow(b, n))) % mod;
}
signed main() {
int T; cin >> T;
while (T --) cout << solve() << endl;
return 0;
}
题意 无向图中子节点数 i i i 为菊花图的顶点的数量,输出 ⊕ i = 1 m a x ( i ) c n t [ i ] \oplus _{i=1}^{max(i)}cnt[i] ⊕i=1max(i)cnt[i]
Tag 快速幂
组合数
逆元
题解 先输入所有节点,得出每个节点的度数,枚举每个节点,计算出 c n t [ i ] , i ∈ [ 2 , m a x ( i ) ] cnt[i],i\in[2,max(i)] cnt[i],i∈[2,max(i)],最后异或即可
#include
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 1e6 + 2, mod = 1e9 + 7;
int n, m, deg[N], cnt[N];
int fac[N], infac[N];
inline int qpow(int a, int b = mod - 2) {
int res = 1;
while (b) {
if (b & 1) res = (ll)res * a % mod;
a = (ll)a * a % mod;
b >>= 1;
}
return res;
}
int C(int n, int m) {
return (ll)fac[n] * infac[m] % mod * infac[n - m] % mod;
}
int solve() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) deg[i] = cnt[i] = 0;
for (int i = 0; i < m; i ++) {
int x, y; cin >> x >> y;
deg[x] ++, deg[y] ++;
}
for (int i = 1; i <= n; i ++)
for (int j = 2; j <= deg[i]; j ++)
(cnt[j] += C(deg[i], j)) %= mod;
ll res = 0;
for (int i = 2; i < n; i ++) res ^= cnt[i];
return res;
}
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
fac[0] = infac[0] = 1;
for (int i = 1; i < N; i ++) fac[i] = (ll)fac[i - 1] * i % mod;
infac[N - 1] = qpow(fac[N - 1]);
for (int i = N - 2; i; i --) infac[i] = (ll)infac[i + 1] * (i + 1) % mod;
int _; cin >> _;
while (_ --) cout << solve() << endl;
return 0;
}