2025-11-17 ZYZ28-NOIP模拟赛-Round7 hetao1733837的record
2025-11-17 ZYZ28-NOIP模拟赛-Round7 hetao1733837的record
好累😩
比赛链接:ZYZ28-NOIP模拟赛-Round7 - ZYZOJ
A.pmst
$( ‵▽′)ψ$$lz$竟然把原来的$T2$塞进了$T1$!是想坑谁?还好我盒到了原。
原题链接:Darnassus
提交链接:07-A - ZYZOJ
分析
思路还是比较简单的,最开始思考方向也是正确的,及由于是排列,差值的绝对值取在$1$到$n-1$,连接$(i,i+1)$的边,其边权一定小于$n$。从极限的角度考虑,$|p_i-p_j|\le \sqrt{n}$,$|i-j|\le \sqrt{n}$,那么,每个点$O(n)$建图,全局$O(n^2)$建图就可以降到$O(\sqrt{n})$,然后跑$Kruskal$复杂度就是$O(n\sqrt{n}logn)$。边权范围$[1,n]$,开桶记录,不必排序,最终复杂度$O(n\sqrt{n}\alpha(n))$。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 50005;
int n, p[N], fa[N], cnt[N];
vector<pair<int, int>> e[N];
int find(int x){
return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
int main(){
freopen("pmst.in", "r", stdin);
freopen("pmst.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> p[i];
cnt[p[i]] = i;
fa[i] = i;
}
int tmp = sqrt(n) + 1;
for (int i = 1; i < n; i++){
for (int j = i + 1; j <= min(n, i + tmp); j++){
long long w = 1ll * (j - i) * abs(p[i] - p[j]);
if (w <= n - 1)
e[w].push_back({i, j});
}
for (int j = i + 1; j <= min(n, i + tmp); j++){
long long w = 1ll * (j - i) * abs(cnt[i] - cnt[j]);
if (w <= n - 1)
e[w].push_back({cnt[i], cnt[j]});
}
}
int cnt = 0;
long long ans = 0;
for (int w = 1; w < n && cnt != n - 1; w++){
for (auto o : e[w]){
int u = find(o.first);
int v = find(o.second);
if (u == v)
continue;
fa[u] = v;
ans += w;
++cnt;
if (cnt == n - 1)
break;
}
}
cout << ans;
}
注意,QOJ本题是多测,需修改读入。
B.cardgame
这题没盒到原/(ㄒoㄒ)/~~
提交链接:07-B - ZYZOJ
题面
小$Y$和小$Z$在卡牌游戏中对战。小$Y$有$N$张卡牌而小$Z$有$M$张卡牌。每张卡牌均有一个由正整数表示的力量值。
每一回合,小$Y$和小$Z$各自展示一张卡牌,如果一名玩家的卡牌力量值大于对手的卡牌力量值,则该玩家被视为胜出此回合。如果展示的两张卡牌具有相同的力量值,则此回合被视为和局。 小$Y$的第$i$张卡牌的力量值为$A_i$。小Z的第$j$张卡牌的力量值为$B_j$。 然后,他们将进行$N \times M$轮的对战。他们都循环地展示卡牌。
小$Y$依照以下顺序展示卡牌(共$M$轮):$A_1 \to A_2 \to \cdots \to A_N \to A_1 \to A_2 \to \cdots \to A_N \to \cdots \to A_1 \to A_2 \to \cdots \to A_N$
小Z依照以下顺序展示卡牌(共$N$轮):$B_1 \to B_2 \to \cdots \to B_M \to B_1 \to B_2 \to \cdots \to B_M \to \cdots \to B_1 \to B_2 \to \cdots \to B_M$
请你求出在这个过程中小$Y$获胜、小$Z$获胜及和局的回合数。
分析
显然思路又对了……为啥没过呢?心态浮躁?代码能力弱?思维还是不行?单纯运气不好?我不知道……但是,前段时间学习方法是绝对有问题的,一直在抄题解,没有自主思考,不热爱思考,必须纠正。
我们很容易想到,两者并不必须走$N\times M$轮,只需走$lcm(N, M)$轮即可,而且,相对应的位置,它们下标一定在模$gcd(N ,M)$下同余。那么,对于每一种,排序,然后二分(实现上也可以用双指针)即可,时间复杂度$O((N+M)log(N+M))$。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m;
signed main() {
freopen("cardgame.in", "r", stdin);
freopen("cardgame.out", "w", stdout);
cin >> n >> m;
vector<int> a(n), b(m);
for (auto &o: a)
cin >> o;
for (auto &o: b)
cin >> o;
int Y = 0, Z = 0;
int GCD = __gcd(n, m);
for (int mod = 0; mod < GCD; mod++) {
vector<int> A, B;
for (int j = mod; j < n; j += GCD)
A.push_back(a[j]);
for (int j = mod; j < m; j += GCD)
B.push_back(b[j]);
sort(A.begin(), A.end());
sort(B.begin(), B.end());
int pntb = 0;
for (auto x: A) {
while (pntb < (int)B.size() && B[pntb] < x)
++pntb;
Y += pntb;
}
int pnta = 0;
for (auto x: B) {
while (pnta < (int)A.size() && A[pnta] < x)
++pnta;
Z += pnta;
}
}
Y *= GCD;
Z *= GCD;
int delta = n * m - Y - Z;
cout << Y << "\n" << Z << "\n" << delta << "\n";
}
操了,场上连二分都写出来了,看了一眼std,tmd连实现都是计算两个再用总数减……
C.jump
操,死机没保存上/ll
题面
比特国由$N$个城市构成,编号为$1,2,\dots,N$。 有$M$条双向道路连接这些城市,第$i$条连通城市$U_i$和城市$ V_i$,通过这条道路需要花费$W_i$的时间。 此外,比特国的人们还可以使用“比特跳跃”来通行于任意两个城市之间。比特跳跃所需的时间取决于两个常数$S$和$K$,两者均为整数。对于从城市$x$跳跃到城市$y$:
· $S = 1$:传送需要$K \times (x & y)$单位时间($&$表示按位与(bitwise AND))。
· $S = 2$:传送需要$K \times (x \oplus y)$单位时间($\oplus$表示按位异或(bitwise XOR))。
· $S = 3$:传送需要$K \times (x | y) $单位时间($| $表示按位或(bitwise OR))。
请你计算从$1$号城市移动到每个城市所需的最短时间。
分析
$O(n^2)$建图显然是很不错的那份手段,但不妨多想一想。
对于$N$是$2$的整数次幂,从$1$跳到$N$,代价为$0$,从$N$跳到任意点,代价均为$0$,输出$N-1$个$0$即可。
S=1
扩展一下,找出最大的$t$使得$2^t-1\le N$,那么,这些点都价值为$0$,剩余只需求补码即可。
但是出现了一个特例,就是说$N=2^{t+1}-1$,那么补码为$0$,需要$O(n)$建边,跑最短路。
S=2
对于异或,如果要从$a$到达$b$,若两者之间某一位二进制位不同,则可以通过$a\oplus 2^?$解决。同理,所以,变成了$O(nlogn)$建边,再跑最短路即可,非常划算。
S=3
或运算是三种位运算中唯一单调递增的,所以,一次跳跃不劣于多次跳跃。除非从其子集而来。那么,从$1$使用$O(n)$建图,先跑一遍$Dijkstra$,再建好每个数二进制下的子集,再跑一遍最短路即可。
正解
STD写得比较扭曲,但是这种写法确实比较整齐但是也挺丑的。
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
template<typename T>
bool chmin(T &x, T val){
if (val < x){
x = val;
return true;
}
return false;
}
template<typename T>
bool chmax(T &x, T val){
if (x < val){
x = val;
return true;
}
return false;
}
int n, m, s;
long long k;
vector<pair<int, long long>> e[N];
long long d[N];
long long son[N];
int main(){
freopen("jump.in", "r", stdin);
freopen("jump.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> s >> k;
for (int i = 1; i <= m; i++){
int u, v;
long long w;
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
memset(d, 0x3f, sizeof(d));
auto dijkstra = [&](){
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> q;
d[1] = 0;
q.push({0, 1});
while (!q.empty()){
long long w = q.top().first;
int u = q.top().second;
q.pop();
if (d[u] != w)
continue;
for (auto tmp : e[u]){
int v = tmp.first;
long long weight = tmp.second;
if (chmin(d[v], w + weight))
q.push({d[v], v});
}
}
};
if (s == 1){
int x = n;
++x;
while (x % 2 == 0)
x /= 2;
if (x == 1){
for (int i = 2; i < n; i++){
e[1].push_back({i, 0});
e[i].push_back({1, 0});
}
for (int i = 1; i < n; i++){
e[i].push_back({n, k * (i & n)});
e[n].push_back({i, k * (i & n)});
}
dijkstra();
for (int i = 2; i <= n; i++)
cout << d[i] << " ";
}
else{
for (int i = 2; i <= n; i++)
cout << 0 << " ";
}
return 0;
}
if (s == 2){
int lg = 1;
while ((1 << lg) <= n)
++lg;
for (int i = 1; i <= n; i++){
for (int j = 0; j < lg; j++){
if ((1 << j) & i){
int x = i ^ (1 << j);
if (x >= 0 && x <= n){
e[i].push_back({x, k * (1 << j)});
e[x].push_back({i, k * (1 << j)});
}
}
}
}
dijkstra();
for (int i = 2; i <= n; i++)
cout << d[i] << " ";
return 0;
}
if (s == 3){
for (int i = 2; i <= n; i++){
e[1].push_back({i, k * (1 | i)});
e[i].push_back({1, k * (1 | i)});
}
dijkstra();
for (int i = 1; i <= n; i++){
son[i] = d[i];
for (int j = 1; j < i; j <<= 1)
if (i & j)
chmin(son[i], son[i ^ j]);
chmin(d[i], son[i] + k * i);
}
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> q;
for (int i = 1; i <= n; i++)
q.push({d[i], i});
while (!q.empty()){
long long w = q.top().first;
int u = q.top().second;
q.pop();
if (d[u] != w)
continue;
for (auto tmp : e[u]){
int v = tmp.first;
long long weight = tmp.second;
if (chmin(d[v], w + weight))
q.push({d[v], v});
}
}
for (int i = 2; i <= n; i++)
cout << d[i] << " ";
}
}
D.interval
提交链接:07-D - ZYZOJ
题面
给定一个长度为$N$的数列$A_1, A_2, \dots, A_N$和一个长度为$N-1$的数列$B_2, B_3, \dots, B_N$。 有$Q$个询问,每次询问给出一个区间$[L_i, R_i]$。请你求出有多少二元组$(l, r)$满足:
· $L_i \leq l < r \leq R_i$
· $\forall i \in {l+1, l+2, \dots, r-1}, A_l > A_i$ (如果$l+1==r$则忽略这一条件,认为符合)
· $ \forall i \in {l, l+1, \dots, r-1}, B_r > A_i $
分析
$N\le 400,Q \le 400$
预处理区间最大值,暴力枚举答案,$O(1)$判断。总复杂度$O(N^2Q)$。
$N \le 3000,Q \le 300$
枚举左端点$l$,找到第一个不小于$A_l$的位置$A_x$,那么$r$最大取到$x$。
查询$r\in [l+1,x]$内$B_r>A_l$的点的个数即可。
可以预处理从每个$l$开始的答案,查询$O(1)$。
总复杂度$O(QNlogN)$或$O(QN)$(预处理每个点不小于自己最近的位置——二分或单调栈)。
$A_i,B_i\le 3$
$l+1=r$的情况预处理,前缀和+差分直接$O(1)$统计。
否则,长度不小于3,唯一的情况就是$A_l=2,B_r = 3$,区间里所有其他的$A_i=1$。
因此对于每个$r$,可能的$l$最多只有1个。
把所有答案放在平面上,扫描线+二维数点。
复杂度$O((N+Q)logn)$。
$2\le N,Q \le 3\times 10^5,1\le L,R \le N,1\le A_i \le 10^9,1\le B_i \le 10^9$
把询问离线到右端点,枚举右端点,统一回答此处的询问。
对于每个$r$,找到最小的$L[r]$使得$\forall i\in [L[r],r-1]$都有$A_i \le B_r$。
可以使用$ST$表+二分。那么对于$r$可能的l就在$[L[r],r-1]$中。
线段树维护,对于此时的最大右端点限制,区间里的可行起点个数。
考虑令$r$为答案数对中的右端点,那么对于每个“当前向右看依然没找到跟自己一样高或者更高”的$A_l$,都可以产生一个贡献。单调栈维护当前这类$A_l$,在线段树维护区间内允许的起点数,每次相当于区间让对应的这些位置答案$+1$。
总复杂度$O((N+Q)logN)$。
正解
#include <bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
typedef long long int64;
typedef pair<int, int> PII;
const int Max_N = 300005;
int64 Ans[Max_N];
vector<PII> qry[Max_N];
int N, Q, A[Max_N], B[Max_N], L[Max_N];
namespace Sparse_table {
int st[20][Max_N], lg[Max_N];
void init() {
lg[0] = -1;
for (int i = 1; i <= N; i++)
lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= N; i++)
st[0][i] = A[i];
for (int i = 1; (1 << i) <= N; i++)
for (int j = 1; j + (1 << i) - 1 <= N; j++)
st[i][j] = max(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
}
int query(int l, int r) {
int k = lg[r - l + 1];
return max(st[k][l], st[k][r - (1 << k) + 1]);
}
}
namespace Segment_tree {
#define lc (root << 1)
#define rc ((root << 1) | 1)
int cnt[Max_N << 2], tag[Max_N << 2];
int64 sum[Max_N << 2];
void pushdown(int root) {
sum[lc] += 1LL * tag[root] * cnt[lc];
sum[rc] += 1LL * tag[root] * cnt[rc];
tag[lc] += tag[root], tag[rc] += tag[root];
tag[root] = 0;
}
void modify(int root, int tl, int tr, int p, int d) {
if (tl == tr) {cnt[root] += d; return;}
pushdown(root);
int mid((tl + tr) >> 1);
if (p <= mid) modify(lc, tl, mid, p, d);
else modify(rc, mid + 1, tr, p, d);
cnt[root] += d;
}
void update(int root, int tl, int tr, int ql, int qr) {
if (ql <= tl && tr <= qr) {
tag[root]++;
sum[root] += cnt[root];
return;
}
pushdown(root);
int mid((tl + tr) >> 1);
if (ql <= mid)
update(lc, tl, mid, ql, qr);
if (qr > mid)
update(rc, mid + 1, tr, ql, qr);
sum[root] = sum[lc] + sum[rc];
}
int64 query(int root, int tl, int tr, int ql, int qr) {
if (ql <= tl && tr <= qr) return sum[root];
pushdown(root);
int64 ret(0);
int mid((tl + tr) >> 1);
if (ql <= mid)
ret += query(lc, tl, mid, ql, qr);
if (qr > mid)
ret += query(rc, mid + 1, tr, ql, qr);
return ret;
}
}
namespace Solve {
void init() {
scanf("%d", &N);
for (int i = 1; i <= N; i++)
scanf("%d", &A[i]);
for (int i = 2; i <= N; i++)
scanf("%d", &B[i]);
scanf("%d", &Q);
for (int l, r, i = 1; i <= Q; i++) {
scanf("%d%d", &l, &r);
assert(l < r);
qry[r].pb(mp(l, i));
}
Sparse_table::init();
for (int i = 2; i <= N; i++) {
int l = 1, r = i - 1, m, ret = i;
while (l <= r) {
m = (l + r) >> 1;
if (Sparse_table::query(m, i - 1) < B[i])
ret = m, r = m - 1;
else
l = m + 1;
}
L[i] = ret;
}
}
void work() {
static int stk[Max_N], top = 0;
for (int i = 1; i < N; i++) {
while (top > 0 && A[ stk[top] ] <= A[i])
Segment_tree::modify(1, 1, N, stk[top--], -1);
Segment_tree::modify(1, 1, N, stk[++top] = i, 1);
if (L[i + 1] != i + 1)
Segment_tree::update(1, 1, N, L[i + 1], i);
for (size_t j = 0; j < qry[i + 1].size(); j++)
Ans[ qry[i + 1][j].sec ] = Segment_tree::query(1, 1, N, qry[i + 1][j].fir, i);
}
for (int i = 1; i <= Q; i++)
printf("%lld\n", Ans[i]);
}
void __main__() {
init();
work();
}
}
int main() {
freopen("interval.in", "r", stdin);
freopen("interval.out", "w", stdout);
Solve::__main__();
return 0;
}
posted on 2025-11-17 19:24 hetao1733837 阅读(0) 评论(0) 收藏 举报
浙公网安备 33010602011771号