题目链接
状态机模型的dp
技能的使用与否作为两种状态
f[i][0]表示进行到第i个事件没有使用技能
f[i][1]表示进行到第i个事件使用了技能
状态转移方程:
LOST事件:
f[i][1] = max (f[i - 1][0], max (0LL, f[i - 1][1] - val)) (第i次使用技能和前i-1次使用技能的最大值)
f[i][0] = max (0LL, f[i - 1][0] - val)
GET事件:
f[i][1] = f[i - 1][1] + val (得到金币一定不需要使用技能所以技能的使用一定是前i-1次用的)
f[i][0] = f[i - 1][0] + val
#include
using namespace std;
typedef long long LL;
const int N = 20010;
LL f[N][2];
int main ()
{
int T; cin >> T;
while (T --)
{
memset (f, 0, sizeof f);
int n; cin >> n;
for (int i = 1; i <= n; i ++)
{
string o; int val;
cin >> o >> val;
if (o[0] == 'L')
{
f[i][1] = max (f[i - 1][0], max (0LL, f[i - 1][1] - val));
f[i][0] = max (0LL, f[i - 1][0] - val);
}
else{
f[i][1] = f[i - 1][1] + val;
f[i][0] = f[i - 1][0] + val;
}
}
cout << max (f[n][0], f[n][1]) << "\n";
}
return 0;
}
题目链接
题目求期望值,等概率,m种技能,选n个并且可重复,所以分母很好确定为m^n次方,对于分子,可以技能选取个数入手,例如某个技能选取i个则情况数位C(n, i) * (m - 1)^(n -i),后面一项的意思是剩下的n-i个技能从剩下的m-1种技能中选择,因为求的是技能伤害,还要乘上伤害i^2,所以最后的结果应为:C(n, i) * (m - 1)^(n -i) * i^2 * m / m^n (1<=i<=n)
#include
using namespace std;
const int N = 1e5 + 10, mod = 1e9 + 7;
typedef long long LL;
LL fac[N], infac[N];
LL qmi (LL a, LL b)
{
LL res = 1;
while (b)
{
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
LL C (LL a, LL b)
{
return fac[a] * infac[b] % mod * infac[a - b] % mod;
}
int main ()
{
fac[0] = infac[0] = 1;
for (int i = 1; i < N; i ++)
{
fac[i] = fac[i - 1] * i % mod;
infac[i] = infac[i - 1] * qmi (i, mod - 2) % mod;
}
int T; cin >> T;
while (T --){
int n, m; cin >> n >> m;
LL res = 0;
for (int i = 1; i <= n; i ++)
res = (res + (LL)i * i % mod * m % mod * C (n, i) % mod * qmi (m - 1, n - i) % mod) % mod;
res = res * qmi (qmi (m, n), mod - 2) % mod;
cout << res << "\n";
}
return 0;
}
题目链接
共有17个格子,可以用二进制枚举每个状态,判断每个状态是不是联通
#include
using namespace std;
const int N = 1e5 + 10, mod = 1e9 + 7;
typedef long long LL;
typedef pair <int, int > PII;
PII p[20];
int cnt[N], g[20][20];
int sum = 0, ans = 0;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
void init()
{
p[0] = {1, 1}, p[1] = {1, 2}, p[2] = {1, 4}, p[3] = {1, 5};
p[4] = {2, 2}, p[5] = {2, 3}, p[6] = {2, 4};
p[7] = {3, 2}, p[8] = {3, 3}, p[9] = {3, 4};
p[10] = {4, 2}, p[11] = {4, 3}, p[12] = {4, 4};
p[13] = {5, 1}, p[14] = {5, 2}, p[15] = {5, 4}, p[16] = {5, 5};
}
void dfs (int x, int y)
{
sum ++;
for (int i = 0; i < 4; i ++)
{
int xx = x + dx[i], yy = y + dy[i];
if (xx < 1 || xx > 5 || yy < 1 || yy > 5 || g[xx][yy] == -1) continue;
g[xx][yy] = -1;
dfs (xx, yy);
}
}
int main ()
{
init ();
int T; cin >> T;
while (T --)
{
ans = 0;
for (int i = 0; i < 17; i ++)
{
cin >> cnt[i];
cnt[i] = (cnt[i] - 1) / 6 + 1;
}
int n; cin >> n;
for (int i = 0; i < 1 << 17; i ++)
{
if (!(i >> 13 & 1)) continue;
memset (g, -1, sizeof g);
vector <int> v;
for (int j = 0; j < 17; j ++)
{
if (i >> j & 1)
{
g[p[j].first][p[j].second] = j;
v.push_back (j);
}
}
sum = 0;
g[p[13].first][p[13].second] = -1;
dfs (p[13].first, p[13].second);
if (sum ==(int) v.size ())
{
int tot = 0;
for (auto t : v)
tot += cnt[t];
if (tot <= n) ans = max (ans, sum);
}
}
cout << ans << "\n";
}
return 0;
}
题目链接
f[i][j]:前i关消耗体力j的最大价值
根据由Ai转移还是Ci转移可分成两种状态:
因为第i-1关不进行操作直接游戏结束,因此第i关一定是由i-1转移,因此可以用滚动数组实现,具体见代码:
f[i][j] = max ( f[i - 1 & 1][j - a[i]] + b[i], f[i - 1 & 1][j - c[i]] + d[i])
#include
using namespace std;
const int N = 1e5 + 10, mod = 1e9 + 7;
typedef long long LL;
typedef pair <int, int > PII;
int a[N], b[N], c[N], d[N];
int f[2][N];
int main ()
{
int T; cin >> T;
while (T --)
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++)
cin >> a[i] >> b[i] >> c[i] >> d[i];
int res = 0;
for (int i = 0; i <= m; i ++) f[0][i] = -1;
f[0][0] = 0;
for (int i = 1; i <= n; i ++)
for (int j = 0; j <= m; j ++)
{
int k = -1;
if (j >= a[i] && f[i - 1 & 1][j - a[i]] >= 0)
k = f[i - 1 & 1][j - a[i]] + b[i];
if (j >= c[i] && f[i - 1 & 1][j - c[i]] >= 0)
k = max (k, f[i - 1 & 1][j - c[i]] + d[i]);
f[i & 1][j] = k;
res = max (res, f[i & 1][j]);
}
cout << res << "\n";
}
return 0;
}
题目链接
根据书的高度由大到小进行插入,id数组维护的是这本书前面的书的位置。
#include
using namespace std;
const int N = 2e5 + 10;
int a[N], id[N], res[N];
int tr[N];
int n, k;
int lowbit (int x)
{
return x & -x;
}
void add (int x, int v)
{
for (int i = x; i <= n; i += lowbit (i)) tr[i] += v;
}
int query (int x)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit (i)) res += tr[i];
return res;
}
int main ()
{
int T; cin >> T;
while (T --)
{
memset (tr, 0, sizeof tr);
memset (res, 0, sizeof res);
cin >> n >> k;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
id[i] = i;
}
sort(id+1,id+n+1,[&](int i,int j){
return a[i]>a[j];
});
for (int i = 1; i <= n; i ++)
{
//由大到小的顺序进行插入
int num = query (id[i]);
if (num < k)
{
res[id[i]] = -1;
add (id[i], 1);
continue;
}
int pre = num - k + 1, l = 1, r = id[i] - 1;
while (l < r)
{
int mid = (l + r) >> 1;
if (query (mid) < pre) l = mid + 1;
else r = mid;
}
res[id[i]] = a[r];
add (id[i], 1);
}
for (int i = 1; i <= n; i ++)
cout << res[i] << "\n";
}
}
题目链接
#include
using namespace std;
string s[7] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
int main ()
{
int n;
cin >> n;
while (n --)
{
int age; string date;
cin >> age >> date;
int hh, mm;
scanf ("%d:%d", &hh, &mm);
if (age >= 18)
{
puts ("Yes");
continue;
}
int i = 0;
for (i = 0; i < 7; i ++)
{
if (s[i] == date)
break;
}
if (i < 4)
{
puts ("No");
continue;
}
if (hh >= 21 || hh < 20)
{
puts ("No");
continue;
}
puts ("Yes");
}
}
题目链接
只需要知道一点:水和岩浆中间没有黑曜石阻隔便可以生成黑曜石
#include
using namespace std;
const int N = 1e5 + 10;
int f[N][2];
struct node {
int hight;
int kind;
}q[N];
bool cmp (node a, node b)
{
return a.hight > b.hight;
}
int main ()
{
int T; cin >> T;
while (T --)
{
int n; cin >> n;
for (int i = 1; i <= n; i ++)
cin >> q[i].kind >> q[i].hight;
sort (q + 1, q + 1 + n, cmp);
int cnt = 0;
int last = -1;
for (int i = 1; i <= n; i ++){
if (q[i].kind == 3)
cnt ++;
if (i == 1) continue;
if ((q[i- 1].kind == 1 && q[i].kind == 2) ||q[i- 1].kind ==2 && q[i].kind ==1 )
cnt ++;
}
cout << cnt << "\n";
}
return 0;
}