spoj day1-2
作者:互联网
A.国王的游戏
题意:
国王和n个大臣分别在左右手写下一个数,,从国王开始按一定顺序,每个大臣获得金币数为其前面包括国王左手的数的乘积除以自己右手写的数,求大臣最多获得金币数的最小值
思路:
这个贪心不是那么容易贪出来。考虑相邻两个大臣i,j的相对位置,其先后顺序只对i,j的金币产生影响
设i,j前面的乘积为x,大臣i,j的金币为 \(w_i,w_j\)
如果先i后j,\(w_{i1} = x / a_i.r, w_{j1} = x * a_i.l / a_j.r\)
如果先j后i,\(w_{j2}= x / a_j.r, w_{i2} = x * a_j.l / a_i.r\)
显然有
\(w_{j2} < w_{j1} , w_{i2} > w_{i1}\)
对答案产生的影响 \(ans_1 = max(w_{i1},w_{j1}), ans_2 = max(w_{i2},w_{j2})\)
若想使产生的影响更小
假设令 \(ans_1 < ans_2\)
那么就有 $ w_{i2} > w_{j1} $
有 $ a_i.l / a_j.r < a_j.l / a_i.r$
即 \(a_i.l *a_i.r < a_j.l * a_j.r\)
所以我们要把左右两项的乘积大的放在后面
因为这题有一些不友好的数据比如2166489661101032350678866897536628698296804147316726878162441737980268621335310233327258927458239967674879428851028800069063620140885606400000000000000000
所以需要使用数组模拟高精度乘除法,数组模拟比较max等运算
代码:
#include <bits/stdc++.h>
#define ll unsigned long long
#define maxn 10005
using namespace std;
int n, s[maxn + 100], t[maxn + 100], ans[maxn + 100];
pair<int, int> p[maxn + 100];
bool compare (pair<int, int> &x, pair<int, int> &y) {
return (ll)(x.first * x.second) < (ll)(y.first * y.second);
}
bool check(int *a, int *b) {
for (int i = maxn; i >= 0; i--) {
if (a[i] > b[i]) return 1;
else if (a[i] < b[i]) return 0;
}
return 1;
}
void multii (int *b, int x) {
for (int i = 0; i <= maxn; i++) b[i] *= x;
for (int i = 0; i <= maxn; i++) {
b[i + 1] += b[i] / 10;
b[i] %= 10;
}
}
void div(int *a, int *b, int x) {
memset(b, 0, sizeof b);
int res = 0;
for (int i = maxn; i >= 0; i--) {
res = res * 10 + a[i];
b[i] = res / x;
res %= x;
}
}
void cpy(int *a, int *b) {
for (int i = 0; i <= maxn; i++) a[i] = b[i];
}
int main () {
cin >> n;
for (int i = 0; i <= n; i++) cin >> p[i].first >> p[i].second;
sort(p + 1, p + n + 1, compare);
s[0] = 1;
for (int i = 0; i <= n; i++) {
div(s, t, p[i].second);
if (check(t, ans)) cpy(ans, t);
multii(s, p[i].first);
}
bool ok = 0;
for (int i = maxn; i >= 0; i--) {
if (ans[i] != 0 && !ok) ok = 1;
if (ok) cout << ans[i];
}
cout << endl;
// for (int i = 1; i <= n; i++) cout << p[i].first << p[i].second << endl;
return 0;
}
B.维护序列
线段树懒标板子题
E.Road Reform
题意:
给定n个定点,m条边,k
构造图的一颗生成树,对于其中的边,进行+-1操作,使得边权值最大的一条正好等于k,求最小操作次数
思路:
贪心的优先选择<k的边
将边从小到大排序
先将边权<k的边加入连通块,如果整张图连通,尝试用一条>=k且绝对值之差更小的边替换
如果不连通,依次用>k的边进行连通。
第二种情况下累加和需要开long long
我写的find函数时间复杂度过高导致TLE了
int find (int x) {
if (fa[x] == x) return x;
else return find(fa[x]);
}
应该写为
int find (int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
确实时间复杂度不太一样
代码:
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 200010
#define maxm 400010
using namespace std;
struct node {
int x, y, z;
} edge[maxm];
int fa[maxn], n, m, k, t;
bool compare (node x, node y) {
return x.z < y.z;
}
int find (int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
void solve() {
int cnt = 0;
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++) scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].z);
sort(edge + 1, edge + m + 1, compare);
for (int i = 1; i <= m; i++) {
if (edge[i].z >= k) continue;
int a = edge[i].x, b = edge[i].y;
a = find(a), b = find(b);
if (a != b) fa[a] = b, cnt++;
}
if (cnt == n - 1) {
int ans = INF;
for (int i = 1; i <= m; i++) ans = min(ans, abs(edge[i].z - k));
printf("%d\n", ans);
}
else {
ll sum = 0;
for (int i = 1; i <= m; i++) {
if (edge[i].z < k) continue;
int a = edge[i].x, b = edge[i].y;
a = find(a), b = find(b);
if (a != b) fa[a] = b, sum += edge[i].z - k;
}
printf("%lld\n", sum);
}
}
int main () {
//ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
scanf("%d", &t);
while (t--) solve();
return 0;
}
G.plant
题意:
找规律
代码:
#include <bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
ll d, res = 1;
ll ksm(ll a, ll b, ll m) {
ll ans = 1;
while (b) {
if (b & 1) ans = (ans * a) % m;
a = (a * a) % m;
b >>= 1;
}
return ans;
}
int main () {
cin >> d;
if (!d) cout << "1" << endl;
else {
res = ksm(2, d - 1, mod) + ksm(2, 2 * d - 1, mod);
cout << res % mod << endl;
}
return 0;
}
标签:return,int,ll,day1,fa,spoj,ans,find 来源: https://www.cnblogs.com/misasteria/p/16468310.html