传送门
作者:hans774882968以及hans774882968
A
简单几何题,用三角函数知识很容易推(对于考上SCU的同学)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 1e6 + 5;
int r, a, b, h;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
read (r, a, b, h);
if (2 * r <= b) {
puts ("Drop");
return 0;
}
double H = 1.0 * h * a / (a - b);
double theta = atan (1.0 * a / 2 / H);
printf ("Stuck\n%.10lf\n", r / sin (theta) - b / (2 * tan (theta) ) );
return 0;
}
B
一看就是博弈dp。先打个表
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 5e3 + 5;
int dp[N][N], a[N];
int n, m;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
bool dfs (int x, int y) {
if (~dp[x][y]) return dp[x][y];
dp[x][y] = false;
if (! (x + y) ) return dp[x][y] = false;
if (!x || !y) return dp[x][y] = true;
if (x == y) return dp[x][y] = true;
if (x < y) return dp[x][y] = dfs (y, x);
rep (k1, 1, x) {
for (int k2 = 0; k2 <= y; k2 += k1) {
dp[x][y] |= !dfs (x - k1, y - k2);
if (dp[x][y]) break;
}
}
rep (k1, 1, y) {
for (int k2 = 0; k2 <= x; k2 += k1) {
dp[x][y] |= !dfs (x - k2, y - k1);
if (dp[x][y]) break;
}
}
return dp[x][y];
}
void bf (int n) {
memset (dp, -1, sizeof dp);
rep (i, 0, n) rep (j, 0, n) dfs (i, j);
// rep (i, 0, n) rep (j, 0, n) cout << dp[i][j] << " \n"[j == n];
rep (i, 0, n) rep (j, 0, n) if (!dp[i][j]) dbg (i, j);
puts ("");
rep (i, 0, n) rep (j, 0, n) if (!dp[i][j] && i < j) dbg (i, j);
}
int main() {
bf (300);
// memset (dp, -1, sizeof dp);
// int lim = 500;
// rep (i, 1, lim) {
// if (a[i]) continue;
// rep (j, i + 1, lim) {
// if (!dfs (i, j) ) {
// a[i] = j;
// break;
// }
// }
// if (a[i]) a[a[i]] = i;
// if (i % 100 == 0) cout << "i=" << i << endl; //dbg
// }
// rep (i, 1, lim) cout << a[i] << " \n"[i == lim];
return 0;
}
发现每一行至多一个false,但没有发现其他规律。因此有一种做法是跑上面那段被注释的代码,把每一行的false的点(可能不存在)给找出来。但是要等很久,算了。
正解是:既然为false的点不超过5000,那么就直接用刷表法。根据博弈dp常识,值为true的点推不出任何点,值为false的点可以推出一系列值为true的点。根据调和级数,复杂度O(n^2*logn)
。说实话这是我第一次看到这种套路。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 5e3 + 5;
bool dp[N][N];
int n, m;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
void init (int n) {
rep (i, 0, n) rep (j, 0, n) {
if (dp[i][j]) continue;
rep (k1, 1, n - i) {
for (int k2 = 0; j + k2 <= n; k2 += k1) {
dp[i + k1][j + k2] = true;
}
}
rep (k1, 1, n - j) {
for (int k2 = 0; i + k2 <= n; k2 += k1) {
dp[i + k2][j + k1] = true;
}
}
}
}
int main() {
init (5000);
int T;
read (T);
while (T--) {
read (n, m);
puts (dp[n][m] ? "Alice" : "Bob");
}
return 0;
}
E
设dp[i]
为当前得到的数集的最大值为i
,能走的步数的期望。再设ans[i]
表示当前得到的数集的最大值为i
,能获得的分数的期望。因为题目中,分数只获得1次,所以我们假设当前存在一个数集的最大值,但并没有拿过分数。显然这一构造性假设是合法的。
没有数,就用“有一个0”来表示,于是ans[0]
即所求。
值得注意的是,我们也可以用数学形式E(x[i])
和E(x[i] ^ 2)
来表示dp[i]
和ans[i]
。
推dp
全期望公式:E(X) = sum(E(X|Y = y[i]) * P(Y = y[i]))
设下一个数为j
。当下一个数小于i
,能走的步数为1;大于等于i
,能走的步数为E(1 + x[j]) = 1 + dp[j]
。故由全期望公式:
dp[i] = sum(p[j])(j < i) + sum(p[j] * (1 + dp[j]))(j >= i)
移项得:
dp[i] = (1 + sum(p[j] * dp[j])) / (1 - p[i]) (j > i)
推ans
当下一个数小于i
,能获得的分数为1。大于等于i
,由上述数学形式得E((1 + x[j]) ^ 2) = E(x[j] ^ 2) + 2 * E(x[j]) + 1 = ans[j] + 2 * dp[j] + 1
。故由全期望公式:
ans[i] = sum(p[j])(j < i) + sum(p[j] * (ans[j] + 2 * dp[j] + 1))(j >= i)
移项得:
ans[i] = (1 + 2 * dp[i] * p[i] + sum(p[j] * (ans[j] + 2 * dp[j]))) / (1 - p[i]) (j > i)
总结:关注期望dp的数学记号!
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 5e3 + 5;
const int mod = 998244353;
LL dp[N], ans[N], p[N];
int a[N];
int n;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
LL q_pow (LL a, LL b) {
LL r = 1;
for (; b; b >>= 1) {
if (b & 1) r = r * a % mod;
a = a * a % mod;
}
return r;
}
int main() {
read (n);
rep (i, 1, n) read (a[i]);
LL isuma = q_pow (accumulate (a + 1, a + n + 1, 0LL), mod - 2);
rep (i, 1, n) p[i] = a[i] * isuma % mod;
dwn (i, n, 0) {
LL s1 = 1, inv1_p = q_pow ( (1 - p[i] + mod) % mod, mod - 2);
rep (j, i + 1, n)
s1 = (s1 + p[j] * dp[j] % mod) % mod;
dp[i] = s1 * inv1_p % mod;
LL s2 = (1 + 2 * p[i] * dp[i] % mod) % mod;
rep (j, i + 1, n)
s2 = (s2 + p[j] * (ans[j] + 2 * dp[j] % mod) % mod) % mod;
ans[i] = s2 * inv1_p % mod;
}
printf ("%lld\n", ans[0]);
return 0;
}
H
a[i]*a[j] = a[k]
,以及数据范围1e6
都提示我们需要枚举倍数。开个桶计数,然后枚举值i
和i
的倍数j
,则贡献为c[i] * c[j] * c[j/i]
。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 1e6 + 5;
int n, c[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
read (n);
int mx = 0;
rep (_, 1, n) {
int x;
read (x);
c[x]++;
mx = max (mx, x);
}
LL ans = 0;
rep (i, 1, mx) {
if (!c[i]) continue;
for (int j = i; j <= mx; j += i) {
ans += 1LL * c[i] * c[j] * c[j / i];
}
}
printf ("%lld\n", ans);
return 0;
}
I
签到。
- x是s的子集,x为1的部分随便取,x为0但s为1的部分固定为1。
- x不是s的子集,个数显然为0。
易错点:x == s
时0也是答案,但题目问的是正整数,要减掉。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int x, s;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
read (x, s);
if (! ( (x & s) == x && (x | s) == s) ) {
puts ("0");
return 0;
}
printf ("%lld\n", (1LL << __builtin_popcount (x) ) - (x == s) );
return 0;
}