考前欢乐赛

A. 炒币

可以贪心峰入谷出

也可以 DP, 取 max 记录前驱

但是因为数太大了,精度会炸

log 降低误差 ,然后就切了》

为啥取 log 能够降低误差?log 与原数不是一一对应吗? double 不是科学计数法吗?

上午炸完的我如是问到。。。

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; bool f = 0; char c = getchar();
	while(!isdigit(c)){f = c == '-'; c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 200005;
double f[maxn];
int n, a[maxn], pre[maxn], p[maxn];
int main(){
	freopen("coin.in","r",stdin);
	freopen("coin.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	f[0] = log(1); int pos = 0;
	for(int i = 1; i <= n; ++i){
		f[i] = f[i - 1]; pre[i] = -1;
		if(f[pos] + log(a[pos]) > f[i] + log(a[i])){
			f[i] = f[pos] + log(a[pos]) - log(a[i]);
			pre[i] = pos;
		}else pos = i;
	}
	for(int i = n; i >= 1; ){
		if(pre[i] == -1){--i;continue;}
		p[i] ^= 1; p[pre[i]] ^= 1; i = pre[i];
	}
	for(int i = 1; i <= n; ++i)printf("%d ",p[i]);
	return 0;
}

B. 凑数

lcm/a=b/gcd 考场写错了 a,b挂惨了

注意到 lcm 的价值取全由 1,a,b 凑出最小的花费

那么如果 ab 单个凑够一个 lcm 可以转化为花费最小的方案,于是可以把 n 缩小到 2lcm 范围内,此时 a,b中较大数是 >=2lcm 的,于是可以枚举

题解做法考虑按照价值排序,假设优先级 a>b>1

那么 a 不会选超过 n/ab 不会选超过 a 个,这两个有一个小于等于 n 可以枚举

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

ll read(){
	ll x = 0; bool f = 0; char c = getchar();
	while(!isdigit(c)){f = c == '-'; c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 200005;

ll n, a, b, x, y, z;

void sol(){
	n = read(), a = read(), b = read();
	x = read(), y = read(), z = read();
	if(x * a <= y && x * b <= z){
		printf("%lld\n",n * x);
		return;
	}
	if(a * z < b * y)swap(a, b), swap(y, z);
	ll mxa = n / a, mxb = a;
	if(mxa > mxb){
		swap(mxa, mxb);
		swap(a, b);
		swap(y, z);
	}
	ll ans = n * x;
	for(int i = 0; i <= mxa; ++i){
		ll res = n - i * a; if(res < 0)break;
		ll k = min(res * x, res / b * z + (res % b) * x);
		ans = min(ans, k + i * y);
	}
	printf("%lld\n",ans);
}

int main(){
	freopen("cs.in","r",stdin);
	freopen("cs.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}
code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

ll read(){
	ll x = 0; bool f = 0; char c = getchar();
	while(!isdigit(c)){f = c == '-'; c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 200005;

ll n, a, b, x, y, z;

void sol(){
	n = read(), a = read(), b = read();
	x = read(), y = read(), z = read();
	ll gcd = __gcd(a, b);
	ll lcm = a / gcd * b, ans = n * x;
	ll aa = lcm / a, bb = lcm / b;
	ll res = n, base = 0;
	if(lcm <= n){
		ll cos = min(aa * y, bb * z);
		cos = min(cos, lcm * x);
		res = (n % lcm) + lcm;
		base = (n / lcm - 1) * cos; 
	}
	if(a < b){
		swap(a, b);
		swap(aa, bb);
		swap(y, z);
	}
	for(ll i = 0; i <= aa; ++i){
		if(i * a > res)break;
		ll k = res - i * a;
		ll p = min(k / b * z + (k % b) * x, k * x);
		ans = min(ans, p + i * y + base);
	}
	printf("%lld\n",ans);
}

int main(){
	// freopen("cs.in","r",stdin);
	// freopen("cs.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

C. 同构

应该多打一点表的。。。

发现质因子集合相同的可以随便排列,然后可以处理一下

另外就是 n/p 相等的 p 因子可以互换,即含 p 因子的数量相同,并且和其他数的互质情况相同,于是也需要乘上阶乘

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; bool f = 0; char c = getchar();
	while(!isdigit(c)){f = c == '-'; c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 1000005;
const int mod = 1e9 + 7;
int n, fac[maxn];
int c[maxn], t[maxn];
int prime[maxn], cnt, mi[maxn];
bool flag[maxn];

int main(){
	freopen("tg.in","r",stdin);
	freopen("tg.out","w",stdout);
	n = read();
	fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	for(int i = 2; i <= n; ++i){
		if(!flag[i])prime[++cnt] = i, mi[i] = i;
		for(int j = 1; j <= cnt && prime[j] * i <= n; ++j){
			flag[i * prime[j]] = 1;
			mi[i * prime[j]] = prime[j];
			if(i % prime[j] == 0)break;
		}
	}
	for(int i = 2; i <= n; ++i)if(flag[i]){
		int p = 1, j = i;
		while(j > 1){
			int m = mi[j]; p *= m;
			while(j % m == 0) j /= m;
		}
		++c[p];
	}else{
		++t[n / i];
		++c[i];
	}
	++t[1];
	int ans = 1;
	for(int i = 1; i <= n; ++i)ans = 1ll * ans * fac[c[i]] % mod;
	for(int i = 1; i <= n; ++i)ans = 1ll * ans * fac[t[i]] % mod;
	printf("%d\n",ans);
	return 0;
}

D. 重建

发现对于非关键点,一定会连到一个关键点上,而关键点之间可以通过高铁转一圈,于是他们不一定直接相连,于是把关键点合并跑一棵最小生成树,这些边是每个城市必须自己解决的,直接统计贡献即可

然后考虑把图缩成了只有若干关键点,对于一个城市,如果直接连接他的关键点,一定也是用最小生成树上的边,于是对新图跑出最小生成树记录用到的边

结合高铁直接建最小生成树,于是你就有了 20 分考场暴力。(我还以为有 40

考虑优化后面的过程,模拟最小生成树

我们会按照边权从小到大考虑是否加边

那么对于相邻的两个城市, bi 较大的城市连的边 bi 较小的城市一定连了

在任意时刻, bi 小的城市的连通性都是更强的

而且如果考虑高铁的连接之后, 两个图的联通性就完全一致,此时对于 bi 较大的图就没有考虑的必要了

那么我们按照 ai 升序进行考虑,每次处理 x,x+1 之间的高铁,以及其中 bi 较大的连边贡献

具体来讲先对边权排序,每次二分出加入边的数量,用前缀和统计贡献

然后算出剩余的联通块数,连上高铁,对于 bi 较大的城市就可以删去了

注意到因为所有车站需要联通,所以 bi 最小的城市必然完整的建出最小生成树,所以统计上其贡献即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; bool f = 0; char c = getchar();
	while(!isdigit(c)){f = c == '-'; c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}
const int maxn = 5000005;
int n, m;
struct edge{
	int u, v, w;
	friend bool operator < (const edge &x, const edge &y){
		return x.w < y.w;
	}
}e[maxn * 5];
int l, r, key[100005], a[100005], b[100005];
int f[maxn], nk[100005], rval[maxn];
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
bool merge(int x, int y){x = fa(x); y = fa(y); if(x == y)return false; f[y] = x; return true;}
bool vis[maxn];
ll sb, sum[maxn];
int p[maxn];
bool cmp(int x, int y){return a[x] < a[y];}
int main(){
	#define int long long
	freopen("rb.in","r",stdin);
	freopen("rb.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; ++i){
		int u = read() + 1, v = read() + 1, w = read();
		e[i] = {u, v, w};
	}
	sort(e + 1, e + m + 1);
	l = read(); for(int i = 1; i <= l; ++i)a[i] = read(), b[i] = read(), sb += b[i];
	r = read(); for(int i = 1; i <= r; ++i)key[i] = read() + 1;
	ll cost = 0; int count = 0;
	for(int i = 1; i <= n; ++i)f[i] = i;
	for(int i = 1; i <= r; ++i)merge(key[1], key[i]);
	for(int i = 1; i <= m; ++i)if(merge(e[i].u, e[i].v))cost += e[i].w, vis[i] = true, ++count;
	cost = cost * l + sb * count;
	for(int i = 1; i <= n; ++i)f[i] = i;
	for(int i = 1; i <= m; ++i)if(vis[i])merge(e[i].u, e[i].v);
	for(int i = 1; i <= r; ++i)nk[fa(key[i])] = i;
	for(int i = 1; i <= n; ++i)nk[i] = nk[fa(i)];
	for(int i = 1; i <= n; ++i)f[i] = i;
	int cnt = 0;
	for(int i = 1; i <= m; ++i){
		int u = nk[e[i].u], v = nk[e[i].v];
		if(merge(u, v))rval[++cnt] = e[i].w;
	}
	for(int i = 1; i <= cnt; ++i)sum[i] = sum[i - 1] + rval[i];
	for(int i = 1; i <= l; ++i)p[i] = i;
	sort(p + 1, p + l + 1, cmp);
	for(int i = 1; i <= l; ++i)f[i] = i;
	for(int i = 1; i < l; ++i){
		int u = p[i], v = u % l + 1;
		u = fa(u); v = fa(v);
		if(b[u] < b[v])swap(u, v);
		int k = upper_bound(rval + 1, rval + r, a[p[i]] - b[u]) - rval - 1;
		cost += 1ll * b[u] * k + sum[k] + 1ll * a[p[i]] * (r - k);
		f[u] = v;
	}
	cost += 1ll * b[fa(1)] * (r - 1) + sum[r - 1];
	printf("%lld\n",cost);
	return 0;
}
posted @   Chen_jr  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示