2022牛客暑期多校训练营4

A - Task Computing

题意

给定 n ( n ≤ 1 0 5 ) n(n≤10^5) n(n105) 个物品,每个物品有 ( w i , p i ) (w_i,p_i) (wi,pi) 两个值。

你需要在这 n n n 个物品中选择 m ( m ≤ 20 ) m(m≤20) m(m20) 个物品,使得这个 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=1mwij=1i1pj 最大。

即第 i i i 个物品的贡献是 w i ∗ w_i* wi i − 1 i-1 i1 个物品的 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+wij=1i1pj+wi+1j=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+1j=1i1pj+wij=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 wij=1i1pj+wi+1j=1ipj>wi+1j=1i1pj+wij=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 wij=1i1pj+wi+1pij=1i1pj>wi+1j=1i1pj+wipi+1j=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+1pi>wi+1+wipi+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(1pi+1)>wi+1(1pi)

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}} 1piwi>1pi+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(mi105) 个工作,每个工作有三个要求 ( 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,ci400)

必须三个要求都达标才能胜任这份工作。

一个人只要能胜任一个公司的任意一份工作就可以去这个公司工作。

询问 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(n100),有 n n n 1 ∗ 1 1 * 1 11 的矩形, n − 1 n-1 n1 1 ∗ 2 1*2 12 的矩形, n − 2 n-2 n2 1 ∗ 3 1*3 13 的矩形…, 1 1 1 1 ∗ n 1*n 1n 的矩形。

把这些矩形拼成一个大矩形,最小化周长。

注意,小矩形只能横着放。

思路

由于总面积是一定的,即 s = w ∗ h s=w*h s=wh 是定值,

要使得周长最小,也就是 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+h22wh ,当 w = h w=h w=h 时取到等号。

所以可以从面积开根开始枚举宽度,验证一下能否拼出 w ∗ h w*h wh 的矩形。

验证时一行一行放,先放大的,长度小的更容易和其它长度拼满。

#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}) Ai1×10ki+xi=Ai(0xi<10ki)

由于 A i ≡ i A_i \equiv i Aii (mod n),则有 ( i − 1 ) × 1 0 k i + x i ≡ i (i-1) \times 10^{k_i} + x_i\equiv i (i1)×10ki+xii (mod n)

x i ≡ i − ( i − 1 ) × 1 0 k i x_i \equiv i-(i-1) \times 10^{k_i} xii(i1)×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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值