A - Task Computing
题意
给定 n ( n ≤ 1 0 5 ) n(n≤10^5) n(n≤105) 个物品,每个物品有 ( w i , p i ) (w_i,p_i) (wi,pi) 两个值。
你需要在这 n n n 个物品中选择 m ( m ≤ 20 ) m(m≤20) m(m≤20) 个物品,使得这个 m m m 个物品的权值
∑ i = 1 m w i ∗ ∏ j = 1 i − 1 p j ∑_{i=1}^{m}w_i∗∏_{j=1}^{i−1}p_j ∑i=1mwi∗∏j=1i−1pj 最大。
即第 i i i 个物品的贡献是 w i ∗ w_i* wi∗ 前 i − 1 i-1 i−1 个物品的 p p p 的乘积。
思路
可以发现 m m m 个物品的顺序不同那么最终贡献不同,所以首先需要确定一个最优的顺序。
- x 1 , . . . , x i , x i + 1 , . . . , x m x_1,...,x_i,x_{i+1},...,x_m x1,...,xi,xi+1,...,xm
A + w i ∗ ∏ j = 1 i − 1 p j + w i + 1 ∗ ∏ j = 1 i p j + B A+w_i∗∏_{j=1}^{i−1}p_j+w_{i+1}∗∏_{j=1}^{i}p_j+B A+wi∗∏j=1i−1pj+wi+1∗∏j=1ipj+B
- x 1 , . . . , x i + 1 , x i , . . . , x m x_1,...,x_{i+1},x_i,...,x_m x1,...,xi+1,xi,...,xm
A + w i + 1 ∗ ∏ j = 1 i − 1 p j + w i ∗ ∏ j = 1 i p j + B A+w_{i+1}∗∏_{j=1}^{i−1}p_j+w_{i}∗∏_{j=1}^{i}p_j+B A+wi+1∗∏j=1i−1pj+wi∗∏j=1ipj+B
若 i i i 放在 i + 1 i+1 i+1 前的贡献更大,则满足:
w i ∗ ∏ j = 1 i − 1 p j + w i + 1 ∗ ∏ j = 1 i p j > w i + 1 ∗ ∏ j = 1 i − 1 p j + w i ∗ ∏ j = 1 i p j w_i∗∏_{j=1}^{i−1}p_j+w_{i+1}∗∏_{j=1}^{i}p_j > w_{i+1}∗∏_{j=1}^{i−1}p_j+w_{i}∗∏_{j=1}^{i}p_j wi∗∏j=1i−1pj+wi+1∗∏j=1ipj>wi+1∗∏j=1i−1pj+wi∗∏j=1ipj
w i ∗ ∏ j = 1 i − 1 p j + w i + 1 ∗ p i ∗ ∏ j = 1 i − 1 p j > w i + 1 ∗ ∏ j = 1 i − 1 p j + w i ∗ p i + 1 ∗ ∏ j = 1 i p j w_i∗∏_{j=1}^{i−1}p_j+w_{i+1}*p_i∗∏_{j=1}^{i-1}p_j > w_{i+1}∗∏_{j=1}^{i−1}p_j+w_{i}∗p_{i+1}*∏_{j=1}^{i}p_j wi∗∏j=1i−1pj+wi+1∗pi∗∏j=1i−1pj>wi+1∗∏j=1i−1pj+wi∗pi+1∗∏j=1ipj
w i + w i + 1 ∗ p i > w i + 1 + w i ∗ p i + 1 w_i+w_{i+1}*p_i > w_{i+1}+w_i*p_{i+1} wi+wi+1∗pi>wi+1+wi∗pi+1
w i ( 1 − p i + 1 ) > w i + 1 ( 1 − p i ) w_i(1-p_{i+1}) > w_{i+1}(1-p_i) wi(1−pi+1)>wi+1(1−pi)
w i 1 − p i > w i + 1 1 − p i + 1 \frac{w_i}{1-p_i} > \frac{w_{i+1}}{1-p_{i+1}} 1−piwi>1−pi+1wi+1
按照这个规则 s o r t sort sort 后得到的新序列中交换任意两位置一定不会更优,所以只需要在该序列中找到一个长度为 m m m的子序列使其贡献最大,直接 d p dp dp。
d p [ i ] [ j ] dp[i][j] dp[i][j] 表示第 i i i 个到第 n n n 个物品中选择了 j j j 个物品得到的最大价值
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
typedef pair<int,int> PII;
typedef long long ll;
const int N = 100010, INF = 0x3f3f3f3f;
int n, m;
double dp[N][25];
struct Node
{
double w;
double p;
}a[N];
bool cmp(Node &a, Node &b)
{
return a.w + b.w * a.p >= b.w + a.w * b.p;
}
void solve()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i].w;
for(int i = 1; i <= n; i++) cin >> a[i].p, a[i].p /= 10000;
sort(a + 1, a + 1 + n, cmp);
dp[n][1] = a[n].w;
for(int i = n - 1; i >= 1; i--)
{
for(int j = 0; j <= m; j++)
{
dp[i][j] = dp[i + 1][j];
if(j && dp[i][j] < dp[i + 1][j - 1] * a[i].p + a[i].w) dp[i][j] = dp[i + 1][j - 1] * a[i].p + a[i].w;
}
}
cout << fixed << setprecision(10) << dp[1][m] << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}
D - Jobs (Easy Version)
题意
有 n n n 个公司,第 i i i 个公司有 m i ( m i ≤ 1 0 5 ) m_i(m_i \le 10^5) mi(mi≤105) 个工作,每个工作有三个要求 ( a i , b i , c i ) ( a i , b i , c i ≤ 400 ) (a_i,b_i,c_i)(a_i,b_i,c_i \le 400) (ai,bi,ci)(ai,bi,ci≤400)。
必须三个要求都达标才能胜任这份工作。
一个人只要能胜任一个公司的任意一份工作就可以去这个公司工作。
询问 q q q 次,每次用随机数种子给出一个三元组,代表一个人三个能力的数值,求这个人可以去多少个公司工作。
强制在线。
思路
对于每个人,我们需要遍历所有的公司,判断这个公司里面是否有一个工作可以满足这个人。
如果这个人的能力为 ( a , b , c ) (a,b,c) (a,b,c) ,我们需要判断是否有一个 i ∈ [ 1 , a ] , j ∈ [ 1 , b ] , k ∈ [ 1 , c ] i∈[1,a],j∈[1,b],k∈[1,c] i∈[1,a],j∈[1,b],k∈[1,c] 的 ( i , j , k ) (i,j,k) (i,j,k) 的职位。
可以用三维前缀和来维护。
p r e [ a ] [ b ] [ c ] pre[a][b][c] pre[a][b][c] 表示是否存在一个工作 ( i , j , k ) (i,j,k) (i,j,k) 满足 i ∈ [ 1 , a ] , j ∈ [ 1 , b ] , k ∈ [ 1 , c ] i∈[1,a],j∈[1,b],k∈[1,c] i∈[1,a],j∈[1,b],k∈[1,c] 。
用 b i t s e t bitset bitset 优化空间。
#include<bits/stdc++.h>
using namespace std;
// #pragma GCC optimize(3)
#define endl '\n'
//#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2000010, mod = 998244353;
int n, q;
bitset<405>pre[11][405][405];
ll power[N];
int solve(int IQ, int EQ, int AQ)
{
int res = 0;
for(int i = 1; i <= n; i++)
if(pre[i][IQ][EQ][AQ]) res++;
return res;
}
void solve()
{
cin >> n >> q;
for(int i = 1; i <= n; i++)
{
int m;
cin >> m;
while(m--)
{
int a, b, c;
cin >> a >> b >> c;
pre[i][a][b][c] = 1;
}
for(int a = 1; a <= 400; a++)
{
for(int b = 1; b <= 400; b++)
{
for(int c = 1; c <= 400; c++)
{
pre[i][a][b][c] = pre[i][a][b][c] | pre[i][a - 1][b][c] | pre[i][a][b - 1][c] | pre[i][a][b][c - 1];
}
}
}
}
int seed;
cin >> seed;
std::mt19937 rng(seed);
std::uniform_int_distribution<> u(1, 400);
power[0] = 1;
for(int i = 1; i <= q; i++) (power[i] = power[i - 1] * seed) %= mod;
ll res = 0;
ll lastans = 0;
for (int i = 1; i <= q; i++)
{
int IQ = (u(rng) ^ lastans) % 400 + 1; // The IQ of the i-th friend
int EQ = (u(rng) ^ lastans) % 400 + 1; // The EQ of the i-th friend
int AQ = (u(rng) ^ lastans) % 400 + 1; // The AQ of the i-th friend
lastans = solve(IQ, EQ, AQ); // The answer to the i-th friend
res += lastans * power[q - i];
res %= mod;
}
cout << res << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}
二维前缀min
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3)
#define endl '\n'
//#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2000010, mod = 998244353;
int n, q;
int p[11][405][405];
ll power[N];
int solve(int a, int b, int c)
{
int res = 0;
for(int i = 1; i <= n; i++)
if(p[i][a][b] <= c) res++;
return res;
}
void solve()
{
memset(p, 0x3f, sizeof p);
cin >> n >> q;
for(int i = 1; i <= n; i++)
{
int m;
cin >> m;
while(m--)
{
int a, b, c;
cin >> a >> b >> c;
p[i][a][b] = min(p[i][a][b], c);
}
for(int a = 1; a <= 400; a++)
{
for(int b = 1; b <= 400; b++)
{
p[i][a][b] = min(p[i][a][b], p[i][a][b - 1]);
p[i][a][b] = min(p[i][a][b], p[i][a - 1][b]);
}
}
}
int seed;
cin >> seed;
std::mt19937 rng(seed);
std::uniform_int_distribution<> u(1, 400);
power[0] = 1;
for(int i = 1; i <= q; i++) (power[i] = power[i - 1] * seed) %= mod;
ll res = 0;
ll lastans = 0;
for (int i = 1; i <= q; i++)
{
int IQ = (u(rng) ^ lastans) % 400 + 1; // The IQ of the i-th friend
int EQ = (u(rng) ^ lastans) % 400 + 1; // The EQ of the i-th friend
int AQ = (u(rng) ^ lastans) % 400 + 1; // The AQ of the i-th friend
lastans = solve(IQ, EQ, AQ); // The answer to the i-th friend
res += lastans * power[q - i];
res %= mod;
}
cout << res << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}
H - Wall Builder II
题意
给定一个 n ( n ⩽ 100 ) n(n \leqslant 100) n(n⩽100),有 n n n 个 1 ∗ 1 1 * 1 1∗1 的矩形, n − 1 n-1 n−1 个 1 ∗ 2 1*2 1∗2 的矩形, n − 2 n-2 n−2 个 1 ∗ 3 1*3 1∗3 的矩形…, 1 1 1 个 1 ∗ n 1*n 1∗n 的矩形。
把这些矩形拼成一个大矩形,最小化周长。
注意,小矩形只能横着放。
思路
由于总面积是一定的,即 s = w ∗ h s=w*h s=w∗h 是定值,
要使得周长最小,也就是 w + h w+h w+h 最小,
有 ( w + h ) 2 = w 2 + h 2 + 2 w h (w+h)^2=w^2+h^2+2wh (w+h)2=w2+h2+2wh ,由均值不等式,
w 2 + h 2 ≥ 2 w h w^2+h^2 \ge 2wh w2+h2≥2wh ,当 w = h w=h w=h 时取到等号。
所以可以从面积开根开始枚举宽度,验证一下能否拼出 w ∗ h w*h w∗h 的矩形。
验证时一行一行放,先放大的,长度小的更容易和其它长度拼满。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
typedef pair<int,int> PII;
typedef long long ll;
const int N = 1000010, INF = 0x3f3f3f3f;
int n;
vector<PII> res[110];
int cur[1100];
bool check(int w, int h)
{
for(int i = 0; i < h; i++) cur[i] = 0;
for(int i = n; i >= 1; i--)
{
res[i].clear();
int cnt = n - i + 1;
for(int j = 1; j <= cnt; j++)
{
bool flag = false;
for(int k = 0; k < h; k++)
{
if(cur[k] + i <= w)
{
res[i].push_back({k, cur[k]});
cur[k] += i;
flag = true;
break;
}
}
if(!flag) return false;
}
}
cout << w + w + h + h << endl;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < n - i + 1; j++)
{
cout << res[i][j].second << " " << res[i][j].first << " " << res[i][j].second + i << " " << res[i][j].first + 1 << endl;
}
}
return true;
}
void solve()
{
cin >> n;
int s = 0;
for(int i = 1; i <= n; i++) s += i * (n - i + 1);
vector<int> v;
for(int w = 1; w * w <= s; w++)
{
if(s % w) continue;
v.push_back(w);
}
for(int i = v.size() - 1; i >= 0; i--)
{
if(check(s / v[i], v[i])) break;
}
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while(T--)
{
solve();
}
return 0;
}
K - NIO’s Sword
题意
玩家初始有一把攻击力为 0 0 0 的剑,需要依次击杀 n n n 个敌人,仅当攻击力模 n n n 与 i i i 同余才能击杀第 i i i 个敌人。玩家可以升级剑,每次升级相当于在当前攻击力后面添加一个数字,问最少需要几次升级。
思路
假设 A i A_i Ai 为击杀第 i i i 个怪物时的攻击力,为了击杀第 i i i 个怪物进行了 k i k_i ki 次升级。
则 A i − 1 × 1 0 k i + x i = A i ( 0 ≤ x i < 1 0 k i ) A_{i-1} \times 10^{k_i} + x_i=A_i(0 \le x_i < 10^{k_i}) Ai−1×10ki+xi=Ai(0≤xi<10ki)
由于 A i ≡ i A_i \equiv i Ai≡i (mod n),则有 ( i − 1 ) × 1 0 k i + x i ≡ i (i-1) \times 10^{k_i} + x_i\equiv i (i−1)×10ki+xi≡i (mod n)
x i ≡ i − ( i − 1 ) × 1 0 k i x_i \equiv i-(i-1) \times 10^{k_i} xi≡i−(i−1)×10ki (mod n)
对于每一个 i i i 值,从小到大枚举 k i k_i ki 的取值,并找到 [ 0 , 1 0 k i ) [0,10^{k_i}) [0,10ki) 内最小非负值 x i x_i xi
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
typedef pair<int,int> PII;
typedef long long ll;
const int N = 1000010, INF = 0x3f3f3f3f;
int n;
ll power[15];
void solve()
{
cin >> n;
if(n == 1)
{
cout << 0 << endl;
return;
}
ll res = 0;
power[0] = 1;
for(int i = 1; i <= 10; i++) power[i] = 10 * power[i - 1];
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= 10; j++)
{
ll k = (i - (i - 1) * power[j] % n + n) % n;
if(k < power[j])
{
res += j;
break;
}
}
}
cout << res << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}