比赛链接:The 2023 ICPC Asia Hangzhou Regional Contest (The 2nd Universal Cup. Stage 22: Hangzhou)
文章目录
D. Operator Precedence(构造)
介于一乘起来就没完没了了,所以尽可能不让乘法里出现大于等于2的数,于是可以想到构造一个类似于:x 2 -1 2 -1 … 2 -1 y
然后让 y 为 1,求 x
(自己这次还是没能想出来,感谢队友)
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
void solve()
{
int n;
cin >> n;
cout << 2 * n - 3 << ' ' << 2 << ' ';
for (int i = 0; i < (2 * n - 4); i ++ )
{
if (i % 2 != 0) cout << 2 << ' ';
else cout << -1 << ' ';
}
cout << -1 << ' ' << 1 << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
}
F. Top Cluster(思维+st表求lca)
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int MAXN = 1e6 + 10;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
struct LCA
{
// 修改maxn 建图后init() 即可使用
const static int maxn = 5e5 + 10;
struct Graph
{
int n; // 点数
vector<vector<PII>> g;
Graph(int n) : n(n), g(n + 1) {};
// 新建单向边
void add_edge(int from, int to, int w)
{
g[from].push_back({to, w});
}
// 新建双向边
void add(int u, int v, int l)
{
add_edge(u, v, l);
add_edge(v, u, l);
}
} G;
int n, idx;
vector<int> dep, dis, seq, fir, _log;
vector<vector<int>> f;
LCA(int n) : n(n), G(n), dep(n + 1), dis(n + 1), seq(maxn << 1), fir(n + 1), f(maxn << 1, vector<int>(20)),
_log(maxn << 1), idx(0) {};
void dfs(int now, int fa)
{
seq[++idx] = now; fir[now] = idx;
dep[now] = dep[fa] + 1;
for (auto [to, w] : G.g[now])
if (to != fa)
dis[to] = dis[now] + w, dfs(to, now), seq[++idx] = now;
}
int mindep(int &x, int &y)
{
return dep[x] < dep[y] ? x : y;
}
void init(int root)
{
dfs(root, 0);
_log[0] = -1;
for (int i = 1; i <= idx; i ++ ) _log[i] = _log[i >> 1] + 1;
for (int i = 1; i <= idx; i ++ ) f[i][0] = seq[i];
for (int j = 1; j < 20; j ++ )
for (int i = 1; i + (1 << j) - 1 <= idx; i ++ )
f[i][j] = mindep(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
// 询问xy的lca
int query(int x, int y)
{
x = fir[x]; y = fir[y];
if (x > y) swap(x, y);
int k = _log[y - x + 1];
return mindep(f[x][k], f[y - (1 << k) + 1][k]);
}
// 询问xy的距离
int getdis(int x, int y)
{
if (x == 0 || y == 0) return 0;
return dis[x] + dis[y] - 2LL * dis[query(x, y)];
}
};
void solve()
{
int n, q; cin >> n >> q;
LCA lca(n);
vector<PII> w(n + 1);
for (int i = 1; i <= n; i ++ )
{
cin >> w[i].first;
w[i].second = i;
}
for (int i = 1; i <= n - 1; i ++ )
{
int u, v, l;
cin >> u >> v >> l;
lca.G.add(u, v, l);
}
sort(w.begin() + 1, w.end());
int pos = n + 1;
for (int i = 1; i <= n; i ++ )
{
if (w[i].first != i - 1)
{
pos = i;
break;
}
}
pos -- ;
lca.init(1);
vector<PII> zj(n + 1); // 直径左右端点
zj[1].first = zj[1].second = w[1].second;
if (n >= 2) zj[2].first = w[1].second, zj[2].second = w[2].second;
for (int i = 3; i <= pos; i ++ )
{
int ld = zj[i - 1].first, rd = zj[i - 1].second;
int zj_len = lca.getdis(ld, rd);
int ver = w[i].second;
int len1 = lca.getdis(ver, ld);
int len2 = lca.getdis(ver, rd);
if (len1 <= zj_len && len2 <= zj_len) zj[i] = zj[i - 1];
else if (len1 > zj_len && len1 > len2) zj[i].first = ld, zj[i].second = ver;
else if (len2 > zj_len && len2 > len1) zj[i].first = ver, zj[i].second = rd;
}
while (q -- )
{
int x, k; cin >> x >> k;
int l = 0, r = pos;
auto check = [&](int mid)
{
if (max(lca.getdis(x, zj[mid].first), lca.getdis(x, zj[mid].second)) <= k) return true;
else return false;
};
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << r << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
while (t--)
{
solve();
}
return 0;
}
G. Snake Move(最短路)
标记一下蛇身在的位置需要多少次之后才能访问(用 flag
标记),其他就正常跑最短路,然后距离在 dd + 1
和 flag[nx][ny] + 1
里取 max 即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
void solve()
{
int n, m, k;
cin >> n >> m >> k;
vector<vector<char>> g(n + 2, vector<char>(m + 2));
vector<vector<int>> flag(n + 2, vector<int>(m + 2));
vector<vector<bool>> st(n + 2, vector<bool>(m + 2));
vector<vector<int>> dist(n + 2, vector<int>(m + 2, INF));
priority_queue<PIII, vector<PIII>, greater<PIII>> pq;
int px, py;
for (int i = 1; i <= k; i ++ )
{
int x, y;
cin >> x >> y;
flag[x][y] = k - i;
if (i == 1)
{
px = x, py = y;
dist[x][y] = 0;
pq.push({0, {x, y}});
}
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
cin >> g[i][j];
while (pq.size())
{
auto t = pq.top();
pq.pop();
int dd = t.first, x = t.second.first, y = t.second.second;
if (st[x][y]) continue;
st[x][y] = true;
for (int i = 0; i < 4; i ++ )
{
int nx = x + dx[i], ny = y + dy[i];
if (nx <= 0 || nx > n || ny <= 0 || ny > m) continue;
if (st[nx][ny] || g[nx][ny] == '#') continue;
if (dist[nx][ny] > max(dd + 1, flag[nx][ny] + 1))
{
dist[nx][ny] = max(dd + 1, flag[nx][ny] + 1);
pq.push({dist[nx][ny], {nx, ny}});
}
}
}
unsigned long long ans = 0;
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ )
{
if (dist[i][j] == INF) continue;
ans = ans + dist[i][j] * dist[i][j];
}
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
}
H. Sugar Sweet II(基环树+概率)
首先我们可以根据题目中给的关系建一个基环森林(也就是 b[i]
向 i
建边,因为 b[i]
的状态会影响 i
要不要加 w[i]
)
然后我们把所有的点分成三类:
a[i] < a[b[i]]
此时b[i]
不管加不加w[b[i]]
,a[i]
都是要加w[i]
的,将dist[i]
标记为 1a[i] >= a[b[i]] + w[b[i]]
此时b[i]
不管加不加w[b[i]]
,a[i]
都是不加w[i]
的,将dist[i]
标记为 0a[b[i]] + w[b[i]] < a[i] < a[b[i]]
此时b[i]
加i
就加,b[i]
不加i
就不加,因为只有这里是不确定的,所以只在这种情况建边
先利用 dfs 算出每个第一类点到第三类点的距离,如果第一类点在随机排序中达到了现在我们建的图这种情况,那么第三类点就要加,那有多大的概率能达到呢?第一类点到第三类点的距离我们在 dfs 中预处理过了,所以就是全排列分之一,即 1 ( d i s t [ i ] ! ) \frac{1}{(dist[i]!)} (dist[i]!)1
之后利用乘法逆元计算答案
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e5 + 10;
const int maxn = 5e6 + 10;
const int mod = 1e9 + 7;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
int Jc[maxn];
void calJc() //求 maxn 以内的数的阶乘 不知道开多少就1e6吧爆不了
{
Jc[0] = Jc[1] = 1;
for(int i = 2; i < maxn; i++) Jc[i] = Jc[i - 1] * i % mod;
}
int pow(int a, int n, int p) // 快速幂取模
{
int ans = 1;
while (n)
{
if (n & 1) ans = ans * a % p;
a = a * a % p;
n >>= 1;
}
return ans;
}
int niYuan(int a, int b) //费马小定理求逆元
{
return pow(a, b - 2, b);
}
void solve()
{
int n;
cin >> n;
vector<int> a(n + 1), b(n + 1), w(n + 1);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i <= n; i ++ ) cin >> b[i];
for (int i = 1; i <= n; i ++ ) cin >> w[i];
vector<vector<int>> g(n + 1);
vector<int> dist(n + 1);
for (int i = 1; i <= n; i ++ )
{
if (a[b[i]] > a[i]) dist[i] = 1;
else if (a[b[i]] + w[b[i]] <= a[i]) dist[0] = 0;
else g[b[i]].push_back(i);
}
function<void(int)> dfs = [&](int u)
{
for (int i = 0; i < g[u].size(); i ++ )
{
int j = g[u][i];
dist[j] = dist[u] + 1;
dfs(j);
}
};
for (int i = 1; i <= n; i ++ )
{
if (dist[i] == 1) dfs(i);
}
vector<int> ans(n + 1);
for (int i = 1; i <= n; i ++ )
{
if (dist[i] == 0) ans[i] = a[i];
else ans[i] = (a[i] + w[i] * niYuan(Jc[dist[i]], mod) % mod) % mod;
cout << ans[i] << ' ';
}
cout << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
calJc();
int t = 1;
cin >> t;
while (t--)
{
solve();
}
}
J. Mysterious Tree(图论交互)
首先关注操作次数 ⌈ n 2 ⌉ + 3 \lceil \frac{n}{2} \rceil + 3 ⌈2n⌉+3 ,看到这就很容易想到那个 n/2 是两两之间连一次吧,如果从头到尾都没碰到相连的边,说明不可能是星形(因为根本没有中间的那个点),一定是链,一旦碰到一个相连的边,就立刻停下
假设这条边的两个端点是 i j,如果 i j 不是 1 2 的话,直接先让 i 和 i - 2 连,有边再让 i 和 i - 1 连,如果还是有边就说明是星形,如果没有边就让 j 和 i - 2、i - 1 连(很容易判断就不详细说了),如果 i j 是 1 2 的话就需要单独注意一下
另外 n 是奇数的时候也要单独注意一下
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
int print(int a, int b)
{
cout << "? " << a << ' ' << b << endl;
int x; cin >> x;
return x;
}
void solve()
{
int n;
cin >> n;
int pos = -1;
for (int i = 1; i + 1 <= n; i += 2)
{
int x = print(i, i + 1);
if (x == 1)
{
pos = i;
break;
}
}
if (pos == -1 && n & 1)
{
int x = print(n - 1, n);
if (x == 1) pos = n - 1;
}
if (pos == -1)
{
cout << "! 1" << endl;
return;
}
if (pos != 1)
{
int x = print(pos, pos - 2);
if (x == 1)
{
int y = print(pos, pos - 1);
if (y == 1)
{
cout << "! 2" << endl;
return;
}
else
{
cout << "! 1" << endl;
return;
}
}
else
{
int y = print(pos + 1, pos - 2);
if (y == 0)
{
cout << "! 1" << endl;
return;
}
else
{
int z = print(pos + 1, pos - 1);
if (z == 1)
{
cout << "! 2" << endl;
return;
}
else
{
cout << "! 1" << endl;
return;
}
}
}
}
else
{
int x = print(1, 3);
if (x == 1)
{
int y = print(1, 4);
if (y == 1)
{
cout << "! 2" << endl;
return;
}
else
{
cout << "! 1" << endl;
return;
}
}
else
{
int y = print(2, 3);
if (y == 0)
{
cout << "! 1" << endl;
return;
}
else
{
int z = print(2, 4);
if (z == 1)
{
cout << "! 2" << endl;
return;
}
else
{
cout << "! 1" << endl;
return;
}
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
}
M. V-Diagram(思维)
首先我们一定要选中间的三个数,然后我们只要再选一个比当前数大的数,整体平均值就一定会上升,(设中间最小坐标是pos)所以答案只可能是 [1, pos + 1]
[pos - 1, n]
[pos - 1, pos +1]
[1, n]
,判断一下取最大即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
void solve()
{
int n;
cin >> n;
vector<int> a(n + 1);
int pos = 0;
for (int i = 1; i <= n; i ++ )
{
cin >> a[i];
if (i != 1 && a[i] > a[i - 1] && pos == 0) pos = i - 1;
}
int sum1 = 0, sum2 = 0;
for (int i = 1; i < pos - 1; i ++ ) sum1 += a[i];
for (int i = pos + 2; i <= n; i ++ ) sum2 += a[i];
double ans = a[pos] + a[pos - 1] + a[pos + 1];
ans = max({1.0 * ans / 3, 1.0 * (ans + sum1) / (pos + 1), 1.0 * (ans + sum2) / (n - pos + 2), 1.0 * (ans + sum2 + sum1) / n});
printf("%.10lf\n", ans);
}
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
}