"蔚来杯"2022牛客暑期多校训练营4 题解

A. Task Computing

考虑答案形式为

\[w_x + \\ w_y \cdot p_x+ \\ w_z \cdot p_x \cdot p_y \]

交换 \(x, y\),要使原答案最大,有 \(w_x + w_y \cdot p_x > w_y + w_x \cdot p_y\),即 \(\frac{w_x}{1-p_x} > \frac{w_y}{1-p_y}\)

发现满足偏序关系,我们可以对 \(\frac{w_i}{1-p_i}\) 进行排序,此时答案转化成选择一个子序列使得答案最大,这个子序列的顺序不会改变。

\(f[i][j]\) 表示 \([i,n]\) 内选了 \(j\) 个数的答案最大值,有 \(f[i][j] = max(f[i+1][j], f[i+1][j-1]*p_i+w_i)\)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
using namespace std;
const int MAXN = 100000 + 5;
const int INF = 0x3f3f3f3f;

struct Node {
	int w, q;
	double p;
}a[MAXN];
int n, m;
double f[MAXN][25];
vector<int> vec[20005];

bool cmp(const Node& a, const Node& b) {
	return a.w + b.w * 1.0 * a.q / 10000.0 > b.w + a.w * 1.0 * b.q / 10000.0;
}

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].q;
	sort(a + 1, a + 1 + n, cmp);
	
	for(int i = 0; i <= n + 1; i++) for(int j = 0; j <= m; j++) f[i][j] = -INF;
	f[n + 1][0] = 0;
	for(int i = n; i >= 1; i--) {
		f[i][0] = 0;
		for(int j = 1; j <= m; j++) f[i][j] = max(f[i + 1][j], f[i + 1][j - 1] * a[i].q / 10000.0 + a[i].w);
		//for(int j = 1; j <= m; j++) cout << f[i][j] << " "; cout << "f\n";
	}
	cout << setprecision(20) << f[1][m] << "\n";
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1; //cin >> T;
	while(T--) Solve();
	return 0;
}

D. Jobs (Easy Version)

\(MapAB[i][j]\) 表示在前两个值为 \(i,j\) 下,第三个值的最小值。

求出所有公司的 \(MapAB\) 后用类似二维前缀和的操作求出 \((1,1)\)\((i,j)\) 这一个矩阵中的最小值。

对于每个询问,枚举每个公司,\(O(1)\) 直接查询第三个值是否满足条件。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#include <random>
using namespace std;

const int MAXN = 100000 + 5;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;

struct Node {
	struct Offer {
		int IQ, EQ, AQ;
	}offer[100005];
	
	int m, MapAB[405][405];
	
	void Init() {
		for(int i = 0; i <= 400; i++) for(int j = 0; j <= 400; j++) MapAB[i][j] = INF;
		for(int i = 1; i <= m; i++) MapAB[ offer[i].IQ ][ offer[i].EQ ] = min(offer[i].AQ, MapAB[ offer[i].IQ ][ offer[i].EQ ]);
		for(int i = 1; i <= 400; i++) {
			for(int j = 1; j <= 400; j++) {
				MapAB[i][j] = min({MapAB[i - 1][j], MapAB[i][j - 1], MapAB[i][j]});
			}
		}
	}
}a[15];

int n, q, seed, res[2000005];

int solve(int IQ, int EQ, int AQ) {
	int cnt = 0;
	for(int i = 1; i <= n; i++) {
		if(a[i].MapAB[IQ][EQ] <= AQ) cnt++;
	}
	return cnt;
}

void Solve() {
	cin >> n >> q;
	for(int i = 1; i <= n; i++) {
		cin >> a[i].m;
		for(int j = 1; j <= a[i].m; j++) cin >> a[i].offer[j].IQ >> a[i].offer[j].EQ >> a[i].offer[j].AQ;
		a[i].Init();
	}
	cin >> seed;
	std::mt19937 rng(seed);
	std::uniform_int_distribution<> u(1,400);
	int 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
		//cout << lastans << " last\n";
		res[i] = lastans; 
	}
	long long ans = 0, cur = 1;
	for(int i = q; i >= 1; i--) {
		ans += 1ll * res[i] * cur % MOD;
		cur = cur * seed % MOD;
		ans %= MOD;
	}
	cout << ans << "\n";
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1; //cin >> T;
	while(T--) Solve();
	return 0;
}

E. Jobs (Hard Version)

我们只考虑前两维,有一个明显的贪心:

当存在 \(offer_i.IQ \leq offer_j.IQ \ \& \ offer_i.EQ \leq offer_j.EQ\) 时,我们可以直接扔掉 \(offer_j\)

按这个贪心筛去不必要的offer,然后按第一维排序,发现此时第二维单调不上升(成阶梯状)。

此时可以设 \(sum\) 数组,每次枚举offer的时候利用差分思想,将所有满足 \(i \geq IQ \ \& \ j \geq EQ\)\((i,j)\) 都加上1,将满足此条件且与前几个offer重合的区域都减去1。之后做个二维前缀和即可。

考虑引入第三维,我们先对offer按第三维从小到大排序,枚举offer时判断两种情况:

① 对于当前 \(offer_i\),若存在 \(IQ_j \leq IQ_i \ \& \ EQ_j \leq EQ_i \ \& \ j<i\),则 \(offer_i\) 能直接被顶替。

② 对于当前 \(offer_i\),若存在 \(IQ_j \geq IQ_i \ \& \ EQ_j \geq EQ_i \ \& \ j<i\),则 \(offer_i\) 能在大于等于 \(AQ_i\) 的部分顶替 \(j\)

对这两个情况进行差分预处理,然后进行三维前缀和,询问时直接输出答案即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#include <random>
using namespace std;

namespace ProblemSolve {
	const int MAXN = 1000000 + 5;
	const int INF = 0x3f3f3f3f;
	const int MOD = 998244353;
	
	struct Offer { int num[3]; };
	int n, q, m, seed, res[MAXN << 1];
	int sum[405][405][405];
	vector<Offer> offers;
	set<pii> Set;
	
	bool cmp(const Offer& a, const Offer& b) { return a.num[2] < b.num[2]; }
	
	bool Cover(pii a, pii b) { return a.fi <= b.fi && a.se <= b.se; }
	
	void Sum(pii pos, pii l, pii r, int b, int k) {
		sum[pos.fi][pos.se][b] += k;
		sum[ max(pos.fi, l.fi) ][ max(pos.se, l.se) ][b] -= k;
		sum[ max(pos.fi, r.fi) ][ max(pos.se, r.se) ][b] -= k;
		sum[ max(l.fi, r.fi) ][ max(l.se, r.se) ][b] += k;
	}
	
	void Del(pii a, int b) {
		Set.erase( Set.find(a) );
		auto pos = Set.upper_bound(a);
		pii r = *pos; pos--;
		pii l = *pos;
		Sum(a, l, r, b, -1);
	}
	
	void Add(pii a, int b) {
		auto pos = Set.upper_bound(a);
		pii r = *pos; pos--;
		pii l = *pos;
		Sum(a, l, r, b, 1);
		Set.insert(a);
	}
	
	void Insert(pii a, int b) {
		auto pos = --Set.upper_bound(a);
		if( Cover(*pos, a) ) return ;
		for(pos = Set.upper_bound(a); Cover(a, *pos); pos = Set.upper_bound(a) ) Del(*pos, b);
		Add(a, b);
	}
		
	void Solve() {
		cin >> n >> q;
		for(int i = 1; i <= n; i++) {
			cin >> m; offers.clear();
			for(int j = 1; j <= m; j++) {
				Offer o; cin >> o.num[0] >> o.num[1] >> o.num[2];
				offers.push_back(o);
			}
			sort(offers.begin(), offers.end(), cmp);
			Set.clear();
			Set.insert({0, 401});
			Set.insert({401, 0});
			for(auto offer : offers) Insert({offer.num[0], offer.num[1]}, offer.num[2]);
			//for(auto i : Set) cout << i.fi << " " << i.se << " set\n";
		}
		
		for(int i = 1; i <= 400; i++) {
			for(int j = 1; j <= 400; j++) {
				for(int k = 1; k <= 400; k++) {
					sum[i][j][k] += sum[i - 1][j][k] + sum[i][j - 1][k] + sum[i][j][k - 1]
						- sum[i - 1][j - 1][k] - sum[i - 1][j][k - 1] - sum[i][j - 1][k - 1]
						+ sum[i - 1][j - 1][k - 1];
				}
			}
		}
		
		cin >> seed;
		std::mt19937 rng(seed);
		std::uniform_int_distribution<> u(1,400);
		int 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=sum[IQ][EQ][AQ];  // The answer to the i-th friend
			//cout << lastans << " last\n";
			res[i] = lastans; 
		}
		long long ans = 0, cur = 1;
		for(int i = q; i >= 1; i--) {
			ans += 1ll * res[i] * cur % MOD;
			cur = cur * seed % MOD;
			ans %= MOD;
		}
		cout << ans << "\n";
	}
}
using namespace ProblemSolve;

signed main()
{
	ProblemSolve::Solve();
	return 0;
}

H. Wall Builder II

构造题,枚举面积 \(S\) 的所有因子作为长度 \(w\),求出高度 \(h\) 并对其进行枚举。

每次枚举贪心地选择最长的砖块填充,直到无法填充或已经填充了长度为 \(w\) 的砖块为止。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#include <random>
using namespace std;

const int MAXN = 100 + 5;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;

int n, S, num[MAXN];
vector<int> vec;

bool Check(int w) {
	int h = S / w;
	//cout << w << " " << h << " : \n";
	for(int i = 1; i <= n; i++) num[i] = n + 1 - i;
	int p = n, i = 1;
	for(i = 1; i <= h; i++) {
		int cur = w;
		//cout << cur << " " << p << " " << num[p] << " num\n";
		while(p && cur >= p * num[p]) {
			cur -= p * num[p], num[p] = 0, p--;
		}
		int q = p;
		//cout << i << " : " << q << " " << cur << " cur\n";
		while(cur && q) {
			while(q && !num[q]) q--;
			if(!q) break;
			
			if(cur >= q && num[q]) num[q]--, cur -= q;
			else q = cur;
		}
		if(cur && !q) return 0;
	}
	return p == 0;
}

void Print(int w) {
	int h = S / w;
	for(int i = 1; i <= n; i++) num[i] = n + 1 - i;
	int p = n, i = 1;
	for(i = 0; i < h; i++) {
		int cur = w;
		while(p && cur >= p * num[p]) {
			for(int j = 1; j <= num[p]; j++) {
				cout << cur - p << " " << i << " " << cur << " " << i + 1 << "\n";
				cur -= p;
			}
			num[p] = 0, p--;
		}
		int q = p;
		while(cur && q) {
			while(q && !num[q]) q--;
			if(!q) break;
			
			if(cur >= q && num[q]) {
				cout << cur - q << " " << i << " " << cur << " " << i + 1 << "\n";
				num[q]--, cur -= q;
			}
			else q = cur;
		}
	}
}

void Solve() {
	cin >> n; S = 0;
	for(int i = 1; i <= n; i++) {
		num[i] = n + 1 - i;
		S += (i * num[i]);
	}
	vec.clear();
	for(int i = 1; i * i <= S; i++) {
		if(S % i == 0) vec.push_back(S / i);
	}
	while(vec.size()) {
		int cur = vec.back(); vec.pop_back();
		if(cur < n) continue;
		if(Check(cur)) {
			cout << 2 * (cur + S / cur) << "\n";
			Print(cur);
			return ;
		}
	}
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1; cin >> T;
	while(T--) Solve();
	return 0;
}

N. Particle Arts

对于方差公式有 \(D(x)=E(x^2)-E(x)^2\)

发现 \(A+B=(A \& B)+(A | B)\),于是 \(\sum{a[i]}\) 被确定,我们考虑如何求得 \(\sum a[i]^2\) 使得方差稳定。

发现对于 \((A \& B)=min(A, B)\) 的情况,每次操作都会重新产生 \(A,B\) 两个数。

我们尽量将二进制不同数位上的1都放在一起,这样产生的数列可以保证二进制数位上具有包含的偏序关系。

tips: 注意极限数据范围

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int __int128
#define fi first
#define se second
#define pii pair<int, int>
using namespace std;
const int MAXN = 100000 + 5;
const int INF = 0x3f3f3f3f;

int n, a[MAXN], sum, x2, num[99];
pii ave, ans;

inline void read(int &X) {
    X = 0; int w = 0; char ch = 0;
    while(!isdigit(ch)) w |= ch=='-', ch = getchar();
    while( isdigit(ch)) X = (X<<3)+ (X<<1) + (ch-48), ch = getchar();
    X = w ? -X : X;
}

inline void write(int x) {
    static int sta[65];
    int top = 0;
    do {
        sta[top++] = x % 10, x /= 10;
    } while(x);
    while(top) putchar(sta[--top] + 48); 
}

pii Gcd(int a, int b) {
	if(a == 0) return {0, 1};
	else {
		int g = __gcd(a, b);
		a /= g, b /= g;
		return {a, b};
	}
}

pii Add(pii a, pii b) {
	int up = a.fi * b.se + b.fi * a.se, down = a.se * b.se;
	a = Gcd(up, down);
	return a;
}

pii Mul(pii a, pii b) {
	a = Gcd(a.fi * b.fi, a.se * b.se);
	return a;
}

pii Mul(pii a, int b) {
	a = Gcd(a.fi * b, a.se);
	return a;
}

void Print(pii ans) {
	write(ans.fi); putchar('/'); write(ans.se);
	//cout << ans.fi << "/" << ans.se << "\n";	
}

void Solve() {
	read(n); sum = 0, x2 = 0;
	for(int i = 1; i <= n; i++) read(a[i]), sum += a[i];
	ave = Gcd(sum, n), ans = {0, 1};
	for(int j = 15; j >= 0; j--) {
		for(int i = 1; i <= n; i++) if(a[i] & (1 << j)) num[j]++;
	}
	for(int i = 1; i <= n; i++) {
		int cur = 0;
		for(int j = 15; j >= 0; j--) if(num[j]) cur += (1 << j), num[j]--;
		x2 += cur * cur;
	}
	ans = Gcd(x2, 1);
	ans = Add(ans, Mul( Mul(ave, ave), n) );
	pii cur = Mul(Mul(ave, 2), sum);
	cur.fi = -cur.fi;
	ans = Add(ans, cur);
	ans.se *= n;
	ans = Gcd(ans.fi, ans.se);
	Print(ans);
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1; //cin >> T;
	while(T--) Solve();
	return 0;
}
posted @ 2022-08-02 22:00  Orzjh  阅读(27)  评论(0)    收藏  举报