题目难度(过题):E A J B L H G F
榜单含打星队仅供参考
铜牌:4 321
银牌:5 419
金牌:9 1119
题意:加法。
// Code Start Here
int t;
cin >> t;
while(t--){
int n , x , a , b;
cin >> n >> x >> a >> b;
cout << x * b + (n - x) * a <<endl;
}
思路:我们考虑这样去最大化a , b:
对于的值是固定的,我们扩大b,减小a可以达到使a *b更大的效果。比如 4 * sqrt ( 1)和1 * sqrt(16) 都是4
所以我们直接让a = 1,b就是原式。
// Code Start Here
int t;
cin >> t;
while(t--){
int x , y;
cin >> x >> y;
cout << 1 << " " << lcm(x , y) / gcd(x , y) << endl;
}
思路:可以发现最后一定和平均数有关,考虑如何求分母,使用逆模运算即可
// Code Start Here
int n;
cin >> n;
int sum = 0;
for(int i = 1;i<=n;i++){
int x;
cin >> x;
sum += x;
sum %= mod;
}
auto mod_pow = [&](int a ,int b)->int{
int res = 1;
a %= mod;
while(b > 0){
if(b & 1)res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
};
int inv = mod_pow(n , mod - 2);
int ans = (sum * inv) % mod;
cout << ans << endl;
思路:思考什么情况下凸多边形面积最小,明显是三角形时面积最小,因此我们考虑如何找一个最小的三角形
观察到题目的三角形很少,而且比较密集,我们考虑可以枚举三角形的三个顶点A,B和C,找最小值,面积计算使用叉乘公式
constexpr double EPS = 1e-9;
template<class T>
struct Point {
T x, y;
Point(T x_ = 0, T y_ = 0) : x(x_), y(y_) {}
Point operator-(const Point& rhs) const { return Point(x - rhs.x, y - rhs.y);}
template<class U> operator Point<U>() const { return Point<U>(static_cast<U>(x), static_cast<U>(y)); }
friend istream& operator>>(istream& is, Point& p) { return is >> p.x >> p.y;}
friend ostream& operator<<(ostream& os, const Point& p) { return os << "(" << p.x << ", " << p.y << ")"; }
static bool line(const Point& a, const Point& b, const Point& c) { return cross(b - a, c - a) == 0; }
double area(const Point& b, const Point& c) const { return fabs(cross(b - *this, c - *this)) / 2.0;}
static long long cross(const Point& a, const Point& b) { return (long long)a.x * b.y - (long long)a.y * b.x;}
};
int32_t main(){
ios_base::sync_with_stdio(false);
cin.tie(NULL);
// Code Start Here
int t;
cin >> t;
while(t--){
int n;
cin >> n;
vector<Point<int>> pts(n);
for (int i = 0; i < n; ++i)
cin >> pts[i];
double min_area = 1e18;
bool found = false;
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j)
for (int k = j + 1; k < n; ++k)
if (!Point<int>::line(pts[i], pts[j], pts[k])) {
double area = pts[i].area(pts[j], pts[k]);
min_area = min(min_area, area);
found = true;
}
if (!found) cout << -1 << endl;
else cout << point(8) << min_area << endl;
}
return 0;
}
题意:
Walk Alone有一个只有正整数的数轴。从整数点a到整数点b的行走成本为lcm(a,b),其中lcm(a,b)表示整数a和b的最小公倍数。由于对整数1的憎恶,Walk Alone禁止任何人移动到小于或等于整数点1的位置。
给定两个整数abb,你需要计算从整数点a到b的最小成本。
思路:直接贪心或者dp没有思路,我们考虑建图处理。
于是题目可以转化为:在一个有n-1个点的带权完全无向图中,两个点之间的权值是lcm(x , y),求两个点之间的最短路。
考虑到数据范围1e7 ,所以Onlogn的堆优化版dijk也没办法跑,我们考虑有没有其他思路。
我们尝试分类讨论,假设a <= b
如果 a=b , cost = 0
如果 b mod a = 0 即b就是a的倍数,直接走到b,cost = b
如果gcd(a , b) > 1,有两种情况:
1. a -> b代价是lcm(a , b)
2. a -> gcd(a , b) -> b代价是a + b
如果gcd(a , b) = 1 , 即a 和 b 没有任何公共因数(即它们的质因子集合是完全不重叠的)
假设a = 15 = 3 *5 , b = 14 = 2 *7
则:gcd(15,14 ) =1
但如果中间走了z = 6。
lcm(15 , 6) = 30,lcm(6,14) = 42,cost = 72
反之,如果中间点是和a、b 都互质的质数(比如 2)2 与15无公共因子。2 与14也只有部分公共因子(但结构简单)
所以我们走中间点只选择小质数,如 2 是最好候选(越小越优)。或者选a 的最小质因数(记作 t1)也可以选b 的最小质因数(记作 t2)
即尝试以下路径:
a → t1 → b
a → t1 → t2 → b
a → t1 → 2 → t2 → b
a → 2 → b
a → t2 → b
// Code Start Here
int t;
cin >> t;
while(t--){
auto check = [&](int x)->int{
for (int i = 2; i * i <= x; ++i)
if (x % i == 0) return i;
return x;
};
int a , b;
cin >> a >> b;
if (a == b) cout << 0 << endl;
else if (b % a == 0) cout << b <<endl;
else if (__gcd(a, b) > 1) {
cout << min((int)a + b, lcm((int)a, (int)b)) <<endl;
}
else{
int t1 = check(a);
int t2 = check(b);
int ans = LLINF;
ans = min(ans, lcm(a, t1) + lcm(t1, b));
ans = min(ans, lcm(a, b));
ans = min(ans, lcm(a, t1) + lcm(t1, t2) + lcm(t2, b));
ans = min(ans, lcm(a, t1) + lcm(t1, 2) + lcm(2, t2) + lcm(t2, b));
ans = min(ans, lcm(a, t1) + lcm(t1, 2) + lcm(2, b));
ans = min(ans, lcm(a, 2) + lcm(2, t2) + lcm(t2, b));
ans = min(ans, lcm(a, 2) + lcm(2, b));
ans = min(ans, lcm(a, 2) + lcm(2, t1) + lcm(t1, t2) + lcm(t2, b));
ans = min(ans, lcm(a, 2) + lcm(2, t1) + lcm(t1, b));
ans = min(ans, lcm(a, t2) + lcm(t2, b));
cout << ans <<endl;
}
}
题意:
有一个大小为 n x m 的网格。 有 k 个位置有鱼,每个位置 (xi, yi) 有 ai (≤ 3) 条鱼。 每次炸弹爆炸可以覆盖其上下左右共最多5个格子,每个格子只能捕一条鱼。 目标:用最少次数把所有鱼都炸干净。
思路:
炸弹每次只能抓一条鱼,而一个格子最多有 3 条鱼,意味着必须对单个格子的鱼数进行状态追踪。
思考状态建模思路:主要是如何高效的提取当前某个格子里的鱼的数量。我们可以用一个数组记录每个鱼格子里剩下的鱼数。例如,a = [2,1,0] 表示第 0 个鱼格子剩 2 条鱼,第 1 个剩 1 条,第 2 个已经没有了。但数组不能直接作为哈希或者数组下标使用,所以我们需要压缩这个数组为一个整数。由于每个格子的鱼数不超过 3,可以视为一个 4 进制数。
对于炸弹效果建模,任意一个炸弹位置 (x, y),它会影响到其上下左右和自身这最多 5 个格子。我们只关心它是否影响到当前的 k 个有鱼格子。对每一个有意义的投放位置,构造出一个向量,标记这颗炸弹能对每个鱼格子造成多大影响(最多 1 条鱼)
// Code Start Here
int n , m , k;
cin >> n >> m >> k;
vector<tuple<int,int,int>> fish(k);
unordered_map<int,int> pos;
vector<vector<int>> effects;
auto encode = [&](vector<int> &a)->int{
int res = 0;
for(int i = k-1;i>=0;i--){
res *= 4;
res += a[i];
}
return res;
};
auto decode = [&](int res)->vector<int>{
vector<int> a(k);
for(int i = 0;i<k;i++){
a[i] = res % 4;
res /= 4;
}
return a;
};
for(int i = 0;i<k;i++){
int x , y , z;
cin >> x >> y >> z;
fish[i] = {x , y , z};
pos[x * 1000 + y] = i;
}
set<pair<int,int>> bomb;
for(auto [x , y , z] : fish){
bomb.insert({x , y});
bomb.insert({x , y+1});
bomb.insert({x , y-1});
bomb.insert({x-1,y});
bomb.insert({x+1,y});
}
for(auto [x , y ] : bomb){
vector<int> effect(k , 0);
for(int i = 0;i<9;i++){
if(abs(dx8[i]) + abs(dy8[i]) <= 1){
int nx = x + dx8[i];
int ny = y + dy8[i];
int key = nx *1000 + ny;
if(pos.count(key)){
effect[pos[key]] = 1;
}
}
}
effects.push_back(effect);
}
int max_val = 1;
vector<int> init(k);
for (int i = 0; i < k; ++i) {
init[i] = get<2>(fish[i]);
max_val *= 4;
}
vector<int> f(max_val, INF);
int st = encode(init);
f[st] = 0;
for (int i = st; i >= 0; i--) {
if (f[i] == INF) continue;
auto cur = decode(i);
for (auto eff : effects) {
vector<int> nxt = cur;
for (int j = 0; j < k; j++) {
if (eff[j] && nxt[j] > 0) {
nxt[j]--;
}
}
int now = encode(nxt);
f[now] = min(f[now], f[i] + 1);
}
}
cout << f[0] << endl;
思路:模拟围棋吃子的过程。对于当前的棋子,找出当前这个棋子所属的连通块,判断这个连通块有没有“气”。如果没有气,把这个块上的所有棋子全部提掉,并统计这个块的大小(也就是吃掉了多少个棋子)。
// Code Start Here
constexpr int N = 20;
int black = 0 , white = 0;
vector<vector<int>> v(N,vector<int>(N,-1));
int q;
cin >> q;
int total = 0;
while(q--){
int x_ , y_;
cin >> x_ >> y_;
int color = (total % 2) ? 0 : 1;
auto bfs = [&](int x , int y ,int cor)->void{
bool vis[N][N] = {0};
vector<pair<int,int>> pos;
queue<pair<int,int>> q;
q.push({x , y});
vis[x][y] = 1;
bool f = false;
while(!q.empty()){
auto[xx , yy] = q.front();
pos.push_back({xx , yy});
q.pop();
for(int i = 0;i<4;i++){
int nx = xx + dx4[i];
int ny = yy + dy4[i];
if(nx >= 1 && ny >= 1 && nx <= 19 && ny <= 19){
if(v[nx][ny] == -1){
f = true;
continue;
}
if(v[nx][ny] != cor)continue;
if(vis[nx][ny])continue;
vis[nx][ny] = true;
q.push({nx , ny});
}
}
}
if(!f){
if(cor)black += pos.size();
else white += pos.size();
for(auto [xx , yy] : pos)v[xx][yy] = -1;
}
};
v[x_][y_] = color;
black = white = 0;
for(int i = 0;i<4;i++){
int nx = x_ + dx4[i];
int ny = y_ + dy4[i];
if(nx >= 1 && ny >= 1 && nx <= 19 && ny <= 19 && v[nx][ny] == 1-color)bfs(nx , ny , 1-color);
}
bfs(x_,y_,color);
cout << black << " " << white << endl;
total++;
}
题目描述较长,本质是一个可回溯可持久化线段树 + 合并模拟。
题意:有一个大小为 n 的数组 a[1..n],表示每本书的初始等级(1~q)。
你要支持以下操作:
1:查询最大等级:将区间 [l, r] 合并到不能再合并,输出最大等级。
2:加一本书 & 合并最大化合并次数:模拟加入一本新等级为 k 的书后,最多能合并多少次?以及代价(每次合并代价为 2^l+1)。
3:单点修改:将某本书改为等级 k。
4:回到历史版本:将当前序列恢复为第 t 次操作后的状态。
const int N = 1e6 + 5;
const int MOD = 1e9 + 7;
int n, m, A, p, q;
int a[N], rt[N];
inline int rnd() {
return A = (7LL * A + 13LL) % 19260817;
}
struct PresidentTree {
static constexpr int MAX_NODE = N * 50;
struct node {
int l = 0, r = 0;
long long sum = 0;
} tr[MAX_NODE];
int cntNodes = 0;
void modify(int prev, int &now, int pos, long long val, int l = 1, int r = n) {
now = ++cntNodes;
tr[now] = tr[prev];
if (l == r) {
tr[now].sum = val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) modify(tr[prev].l, tr[now].l, pos, val, l, mid);
else modify(tr[prev].r, tr[now].r, pos, val, mid + 1, r);
tr[now].sum = tr[tr[now].l].sum + tr[tr[now].r].sum;
}
long long query(int now, int L, int R, int l = 1, int r = n) {
if (!now || R < l || L > r) return 0;
if (L <= l && r <= R) return tr[now].sum;
int mid = (l + r) >> 1;
return query(tr[now].l, L, R, l, mid) + query(tr[now].r, L, R, mid + 1, r);
}
} T;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> A >> p >> q;
for (int i = 1; i <= n; ++i) {
a[i] = rnd() % q + 1;
T.modify(rt[0], rt[0], i, 1LL << a[i]);
}
for (int i = 1; i <= m; ++i) {
int opt = rnd() % p + 1;
if (opt == 1) {
rt[i] = rt[i - 1];
int l = rnd() % n + 1, r = rnd() % n + 1;
if (l > r) swap(l, r);
long long sum = T.query(rt[i], l, r);
int highest = 0;
for (int j = 60; j >= 0; --j) {
if ((sum >> j) & 1) {
highest = j;
break;
}
}
cout << highest << '\n';
} else if (opt == 2) {
rt[i] = rt[i - 1];
int l = rnd() % n + 1, r = rnd() % n + 1;
if (l > r) swap(l, r);
int k = rnd() % q + 1;
long long sum = T.query(rt[i], l, r);
long long cost = 0;
for (int j = k; j <= 60; ++j) {
if ((sum >> j) & 1) {
cost = (cost + (1LL << (j + 1))) % MOD;
} else {
break;
}
}
cout << cost << '\n';
} else if (opt == 3) {
int pos = rnd() % n + 1;
int k = rnd() % q + 1;
T.modify(rt[i - 1], rt[i], pos, 1LL << k);
} else {
int t = rnd() % i;
rt[i] = rt[t];
}
}
return 0;
}