第二周训练题单
多项式输出
小细节比较多
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
for (int x , i = n ; i >= 0; i--) {
cin >> x;
if( x == 0 ) continue;
if (x < 0) cout << "-", x = -x;
else if( i != n ) cout << "+";
if (x > 1 || i == 0 ) cout << x;
if( i > 1 ) cout << "x^" << i;
else if( i == 1 ) cout << "x";
}
return 0;
}
铺地毯
根本不需要维护整张图,读入所有地毯倒序枚举判断一下就好了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
vector<int> a(n+1) , b(n+1) , c(n+1) , d(n+1);
for( int i = 1 ; i <= n ; i ++ )
cin >> a[i] >> b[i] >> c[i] >> d[i] , c[i] +=a[i] , d[i] += b[i];
int x , y;
cin >> x >> y;
for( int i = n ; i >= 1 ; i -- )
if( a[i] <= x && x <= c[i] && b[i] <= y && y <= d[i] )
cout << i , exit(0);
cout << "-1\n";
return 0;
}
第k小
因为 k 值不变,所以直接维护大小为 k 的堆就好了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
priority_queue<int> q;
for (int x; n; n--)
cin >> x, q.emplace(x);
while (q.size() > k) q.pop();
for( int op , x ; m ; m -- ){
cin >> op;
if( op == 1 ){
cin >> x , q.emplace(x);
while (q.size() > k) q.pop();
}else{
if(q.size() < k ) cout << "-1\n";
else cout << q.top() << "\n";
}
}
return 0;
}
丢手绢
实际上,在总长度不超过半径时,问任意子区间的最大值,要注意的是,这里是环,所以要进行破环成链,这里我采用了循环坐标,当然也可使用存两遍。区间最大值用双指针就能很好的解决。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, C = 0;
cin >> n;
vector<int> a(n);
for (auto &i: a)
cin >> i, C += i;
int res = 0;
for (int l = 0, r = -1, cnt = 0; l < n ; l++) {
while ( cnt * 2 <= C) {
r = ( r + 1 ) % n , cnt += a[r];
res = max(res, min(cnt, C - cnt));
}
cnt -= a[l];
}
cout << res << "\n";
return 0;
}
中位数图
规定 x 是子区间内比 b 大的数的数量,y 是子区间内比 b 小的数的数量
首先我们统计 b 的一侧,每个位置上x-y的值。然后再遍历另一侧,对于当前的x,y,看另一侧有多少y-x
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
#define mp make_pair
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, m, P = -1, res = 0;
cin >> n >> m;
vector<int> a(n);
for (auto &i: a) cin >> i;
for (int i = 0; P == -1 && i < n; i++)
if (a[i] == m) P = i;
map<int, int> cnt;
for (int i = P, x = 0, y = 0; i >= 0; i--) {
if (a[i] > m) x++;
if (a[i] < m) y++;
cnt[x - y]++;
}
for (int i = P, x = 0, y = 0; i < n; i++) {
if (a[i] > m) x++;
if (a[i] < m) y++;
res += cnt[y - x];
}
cout << res << "\n";
return 0;
}
好串
括号匹配
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
#define mp make_pair
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
string s;
cin >> s;
int cnt = 0;
for (auto i: s) {
if (i == 'a') cnt++;
else cnt--;
if (cnt < 0) {
cout << "Bad\n";
return 0;
}
}
if (cnt == 0) cout << "Good\n";
else cout << "Bad\n";
return 0;
}
兔子的区间密码
在二进制下考虑,从高位开始枚举,遇到第一位l!=r
的,一个数当前位置 1 其他位置 0,另一个数当前位置 0 其他位置 1。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void solve(){
int l = read() , r = read();
int i = 63;
for( ; i >= 0 ; i -- )
if( (l>>i) != (r>>i) ) break;
printf("%lld\n" , (1ll << (i+1))-1 );
return;
}
int32_t main() {
for(int T = read(); T ; T--)
solve();
return 0;
}
道路建设
最小生成树
#include <bits/stdc++.h>
using namespace std;
struct dsu {
vector<int> fa;
dsu(int n = 0) : fa(n + 1, -1) {};
int getfa(int x) {
if (fa[x] < 0) return x;
return fa[x] = getfa(fa[x]);
}
bool merge(int x, int y) {
x = getfa(x), y = getfa(y);
if (x == y) return false;
if (fa[x] > fa[y]) swap(x, y);
fa[x] += fa[y], fa[y] = x;
return true;
}
int size(int x) {
x = getfa(x);
return -fa[x];
}
};
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int c, n, m;
cin >> c >> m >> n;
dsu d(n);
vector<tuple<int, int, int>> e(m);
for (auto &[w, x, y]: e)
cin >> x >> y >> w;
sort(e.begin(), e.end());
for (const auto &[w, x, y]: e)
if (d.merge(x, y)) c -= w;
if (c >= 0 && d.size(1) == n ) cout << "Yes\n";
else cout << "No\n";
return 0;
}
逛公园
反向建图跑最短路,正向记忆化搜索。
f[i][j]
表示从起点走到点i
多花费了j
的方案数。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int n, m, k, P, flag;
vector<int> dis;
vector<vector<pair<int, int>>> e, g;
vector<vector<int>> f;
vector<vector<bool>> cnt;
void dij() {
dis = vector<int>(n + 1, INT_MAX);
dis[n] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
q.emplace(0, n);
vector<bool> vis(n + 1, false);
while (!q.empty()) {
auto [d, u] = q.top();
q.pop();
if (vis[u]) continue;
vis[u] = true;
for (auto [v, w]: g[u]) {
if ( dis[v] <= d + w) continue;
dis[v] = d + w, q.emplace(dis[v], v);
}
}
return;
}
int calc(int u, int t) {
if (cnt[u][t]) {
flag = 0;
return 0;
}
if (f[u][t] != -1) return f[u][t] % P;
cnt[u][t] = true;
f[u][t] = 0;
for (const auto &[v, w]: e[u]) {
int p = t - (dis[v] + w - dis[u]);
if (p >= 0) f[u][t] = (f[u][t] + calc(v, p)) % P;
if (flag == 0) return 0;
}
cnt[u][t] = false;
if (u == n && t == 0) f[n][0] = 1;
return f[u][t] % P;
}
void solve() {
n = read(), m = read(), k = read(), P = read();
e = vector<vector<pair<int, int>>>(n + 1), g = vector<vector<pair<int, int>>>(n + 1);
for (int u, v, w; m; m--) {
u = read(), v = read(), w = read();
e[u].emplace_back(v, w), g[v].emplace_back(u, w);
}
dij();
f = vector<vector<int>>(n + 1, vector<int>(k + 1, -1));
flag = 1;
cnt = vector<vector<bool>>(n + 1, vector<bool>(k + 1, false));
int res = 0;
for (int i = 0; flag && i <= k; i++)
res = (res + calc(1, i)) % P;
if (flag == 0) res = -1;
printf("%lld\n", res);
return;
}
int32_t main() {
for (int T = read(); T; T--)
solve();
return 0;
}
求先序排列
二叉树还原
#include <bits/stdc++.h>
using namespace std;
#define int long long
string getPrefer(string middle, string suffer) {
if (middle.size() <= 1 ) {
return middle;
}
int p;
for (p = 0; p < middle.size(); p++)
if (middle[p] == suffer.back()) break;
string lMiddle = middle.substr(0, p);
string rMiddle = middle.substr(p + 1);
string lSuffer = suffer.substr(0, p);
string rSuffer = suffer.substr(p , rMiddle.size());
return suffer.back() + getPrefer(lMiddle, lSuffer) + getPrefer(rMiddle, rSuffer);
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
string middle, suffer;
cin >> middle >> suffer;
cout << getPrefer(middle, suffer);
}
数字组合
改变枚举的顺序
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
vector<int> a (n) , b(n) , c(n) , d(n);
for( int i = 0 ; i < n ; i ++ )
cin >> a[i] >> b[i] >> c[i] >> d[i];
map<int,int> cnt;
for( auto i : a )
for( auto j : b )
cnt[ i + j ] ++;
int res =0;
for( auto i : c )
for( auto j : d )
res += cnt[ - i - j ];
cout << res << "\n";
return 0;
}
Protecting the Flowers
贪心
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
#define int long long
struct node{
int t , d;
};
bool cmp( node a , node b ){
return a.t * b.d < b.t * a.d;
}
signed main() {
int n , res = 0 , cnt = 0;
cin >> n;
vector<node> a(n);
for( int i = 0 ; i < n ; i ++ )
cin >> a[i].t >> a[i].d;
sort( a.begin() , a.end() , cmp );
for( int i = 0 ; i < n ; i ++ ){
res += cnt * a[i].d , cnt += 2ll * a[i].t;
}
cout << res;
return 0;
}
小咪买东西
\[\frac{\sum v}{\sum c } = \max\\
\sum v - \max\sum c = \sum( v - \max c) = 0
\]
这样的话我们可以二分\(\max\),如果枚举的值是\(x\),先计算出\(y_i=v_i-xc_i\)然后选择前 k 大,判断和和零的大小即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const double eps = 1e-6;
void solve() {
int n, k;
cin >> n >> k;
vector<double> c(n), v(n);
for (int i = 0; i < n; i++)
cin >> c[i] >> v[i];
auto f = [n, k, c, v](double x) {
vector<double> y;
for (int i = 0; i < n; i++)
y.push_back(v[i] - x * c[i]);
sort(y.begin(), y.end(), greater<int>());
double cnt = 0;
for (int i = 0; i < k; i++)
cnt += y[i];
return cnt < 0 ;
};
double l = 0, r = 1e9, mid, res = -1;
while ( r-l > eps ) {
mid = (l + r) / 2.0;
if (f(mid)) res = mid, r = mid - eps;
else l = mid + eps;
}
cout << (int)res << "\n";
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
星球大战
模拟
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m;
map<int, multiset<int>> cntX, cntY;
for (int x, y; n; n--) {
cin >> x >> y;
cntX[x].emplace(y);
cntY[y].emplace(x);
}
for (int c, d; m; m--) {
cin >> c >> d;
if (c == 0) {
cout << cntX[d].size() << "\n";
for (auto y: cntX[d])
cntY[y].erase(cntY[y].lower_bound(d));
cntX[d].clear();
} else {
cout << cntY[d].size() << "\n";
for (auto x: cntY[d])
cntX[x].erase(cntX[x].lower_bound(d));
cntY[d].clear();
}
}
return 0;
}
叠积木
用并查集维护合并的过程,同时额外维护当前点到祖先节点的距离,注意在路径压缩的时候更新距离
#include <bits/stdc++.h>
using namespace std;
#define int long long
struct dsu {
vector<int> fa, dis;
dsu(int n = 0) : fa(n + 1, -1), dis(n + 1, 0) {};
int getFa(int x) {
if (fa[x] < 0) return x;
int t = fa[x], p = getFa(fa[x]);
dis[x] += dis[t], fa[x] = p;
return fa[x];
}
void merge(int x, int y) {
x = getFa(x), y = getFa(y);
dis[x] = -fa[y];
fa[y] += fa[x], fa[x] = y;
}
int d(int x) {
getFa(x);
return dis[x];
}
};
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int q;
cin >> q;
dsu d(30000);
while (q--) {
string op;
cin >> op;
if (op == "M") {
int x, y;
cin >> x >> y;
d.merge(x, y);
} else {
int x;
cin >> x;
cout << d.d(x) << "\n";
}
}
return 0;
}
传送带
考虑到精度要求不高,直接暴力的把线段分成5000个点,然后暴力枚举
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef pair<double, double> vec;
vec operator-(vec a, vec b) {
a.first -= b.first, a.second -= b.second;
return a;
}
vec operator+(vec a, vec b) {
a.first += b.first, a.second += b.second;
return a;
}
vec operator/(vec a, double x) {
a.first /= x, a.second /= x;
return a;
}
double dis(vec a, vec b) {
return sqrt((a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second));
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
vec A, B, C, D;
double p, q, r;
cin >> A.first >> A.second >> B.first >> B.second >> C.first >> C.second >> D.first >> D.second >> p >> q >> r;
auto d = (B - A) / 5000;
vector<vec> t;
int ct = 5000;
for (auto i = A; ct; ct--, i = i + d)
t.push_back(i);
t.push_back(B);
d = (D - C) / 5000;
vector<vec> s;
ct = 5000;
for (auto i = C; ct; ct--, i = i + d)
s.push_back(i);
s.push_back(D);
double res = LDBL_MAX;
for (auto i: t)
for (auto j: s) {
double cnt = dis(A, i) / p + dis(i, j) / r + dis(j, D) / q;
res = min(res, cnt);
}
cout << fixed << setprecision(2) << res << "\n";
return 0;
}
Look Up
单调栈?
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
stack<int> s;
vector<int> res(n + 1, 0);
for (int i = 1; i <= n; i++) {
while (!s.empty() && a[s.top()] < a[i])
res[s.top()] = i, s.pop();
s.push(i);
}
for (int i = 1; i <= n; i++)
cout << res[i] << "\n";
return 0;
}