2026-10-19 hetao1733837 的刷题记录

QOJ1813 排列的乐趣

原题链接:排列的乐趣

分析

我初步的思路是在两端询问 2,中间依次询问 1,然后差分约束建图,尝试跑出来一个唯一的序列。

但是,如何确定一个唯一确定的序列呢?

昨天晚上想了想,我可能会一个 n l o g n nlogn nlogn 次询问 1 的,就是,从中间选一个数,进行询问,把大于的扔到一边,小于的扔到一边,然后每一边分别递归下去......别急......我思考一下......如何从中位数知道剩下两个分别的大小呢?

抓住了什么......

诶,从某些角度而言,我通过 1 操作无法确定的只有最大值和最小值,那么,这个 2 是不是就是为此准备的呢?那么,我需要使用 n + 2 n+2 n+2 次 1 询问和 2 2 2 次 2 询问得知最大值和最小值的位置,然后,再使用 n − 2 n-2 n−2 次操作把每个非最大值、最小值的位置询问出来,就做完了。这个,由于这个是中位数,是排序的,所以说,我们根本没必要去思考最大值有多个,最小值有多个的情况。也不可能有多个,这个是一个排列......

那么,我们先进性 n − 2 n-2 n−2 次询问,然后依次每连续 3 3 3 个询问一次。然后,我们得出中位数的最大值和最小值,则最大值和最小值一定在这两个区间内。对吗?应该有满足这个的吧......然后,我们可以询问两端的大小关系,得到最大值和最小值的确切位置,然后,我们对于整个序列跑一遍 1 操作......就做完了?好像真的是对的......


这个是错误的,对于排列:

cpp 复制代码
2 3 4 1 9 5 6 7 8

就可以轻松卡掉......但是,题目真的保证了这个东西唯一确定吗?

天啊,这个是真的......有点厉害了......就是说,我人为的选择哨兵,然后,不断进行更新,似乎就可以得到了!

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 60005;
int n;
int ans[N];
bool vis[N];
int query1(int i, int j, int k){
    int res;
    cout << "? 1 " << i << " " << j << " " << k << endl;
    cin >> res;
    return res;
}
int query2(int i, int j){
    int res;
    cout << "? 2 " << i << " " << j << endl;
    cin >> res;
    return res;
}
void answer(){
    cout << "! ";
    for (int i = 1; i <= n; i++){
        cout << ans[i] << " ";
    }
    cout << endl;
}
signed main(){
    cin >> n;
    int tmp1 = query1(1, 2, 3);
    int tmp2 = query1(1, 2, 4);
    int tmp3 = query1(1, 3, 4);
    int mn_pos, mmn_pos, mmx_pos, mx_pos;
    if (tmp1 == tmp2){
        if (tmp1 < tmp3){
            mn_pos = 1; 
            mmn_pos = 2; 
            mmx_pos = 3; 
            mx_pos = 4;
        }
        else{
            mn_pos = 3; 
            mmn_pos = 4; 
            mmx_pos = 1; 
            mx_pos = 2;
        }
    }
    else if (tmp1 == tmp3){
        if (tmp1 < tmp2){
            mn_pos = 1; 
            mmn_pos = 3; 
            mmx_pos = 2; 
            mx_pos = 4;
        }
        else{
            mn_pos = 2; 
            mmn_pos = 4; 
            mmx_pos = 1; 
            mx_pos = 3;
        }
    }
    else{
        if (tmp1 < tmp2){
            mn_pos = 2; 
            mmn_pos = 3; 
            mmx_pos = 1; 
            mx_pos = 4;
        }
        else{
            mn_pos = 1; 
            mmn_pos = 4; 
            mmx_pos = 2; 
            mx_pos = 3;
        }
    }
    int cur_mmn = min({tmp1, tmp2, tmp3});
    int cur_mmx = max({tmp1, tmp2, tmp3});
    for (int i = 5; i <= n; i++){
        int tmp = query1(mn_pos, mmx_pos, i);
        if (tmp < cur_mmn){
            ans[mmn_pos] = cur_mmn;
            mmn_pos = i;
            cur_mmn = tmp;
        }
        else if (tmp == cur_mmn){
            ans[mn_pos] = cur_mmn;
            mn_pos = i;
            cur_mmn = query1(mn_pos, mmn_pos, mmx_pos);
        }
        else if (tmp < cur_mmx){
            ans[i] = tmp;
        }
        else if (tmp == cur_mmx){
            ans[mmx_pos] = cur_mmx;
            mmx_pos = i;
            cur_mmx = query1(mmn_pos, mmx_pos, mx_pos);
        }
        else{
            ans[mx_pos] = cur_mmx;
            mx_pos = i;
            cur_mmx = tmp;
        }
    }
    if (query2(mn_pos, mmn_pos) == mmn_pos)
        swap(mn_pos, mmn_pos);
    if (query2(mmx_pos, mx_pos) == mx_pos)
        swap(mmx_pos, mx_pos);
    ans[mmn_pos] = cur_mmn;
    ans[mmx_pos] = cur_mmx;
    for (int i = 1; i <= n; i++){
        vis[ans[i]] = true;
    }
    for (int i = 1; i <= n; i++){
        if (!vis[i]){
            if (!ans[mn_pos]){
                ans[mn_pos] = i;
            }
            else{
                ans[mx_pos] = i;
            }
        }
    }
    answer();
    return 0;
}

LGP9907 COCI 2023/2024 #1 Mostovi

原题链接:COCI 2023/2024 #1 Mostovi

分析

难道说把边看成点,把点看成边进行重构,然后,新图上的一些"关键点"就是原图上的重要的边......这个并不好抽象啊,感觉做不了......

那这个能评黑吗?

思考暴力怎么打......就是说,这个我会一个 O ( m n l o g n ) O(mnlogn) O(mnlogn) 的枚举边 + + + 并查集的做法。这个可以获得 30 p t s 30pts 30pts......试一下吧。好的,非常完美啊......


这个暴力显然对于正解并没有什么启发性......


上了一节生物课,xy 送了我一本高等数学期末卷子。里面有一道题出处是"(山东某 985 高校)",这和直接说出来有什么区别?


开一个题解吧......

对吧,这种删边你最好找一棵树做。

那么,我们对于图,你就找一棵 dfs 树呗,


不不不,这个有点过于太难了,我决定先放弃。

LGP17279 『__OI R1』Little Circle

原题链接:『__OI R1』Little Circle

分析

四连通块是指在一个格子或像素区域中,每个格子通过上下左右四个方向相连形成的连通区域。

就是要保证联通是吗?

我们发现,对于 d = 1 d=1 d=1,我们想要构造尽可能多的类似下面的结构:

对于 d = 2 d=2 d=2,我们想要构造尽可能多的类似下面的结构:

对于 d = 3 d=3 d=3,我们想要构造尽可能多的类似下面的结构:

对于 d = 4 d=4 d=4,我们想要构造尽可能多的类似下面的结构:

那么,这个似乎找个规律直接就可以算了......

D D D 老师发力了......

需要勘误的是,在 d = 1 d=1 d=1 的情况下:

  • n = 1 n=1 n=1,输出 0 0 0
  • n = 2 n=2 n=2 或 n = 3 n=3 n=3,输出 2 2 2
  • n ≡ 0 ( m o d 4 ) , n ≥ 4 n \equiv 0 \pmod{4},n\ge 4 n≡0(mod4),n≥4,输出 ⌊ n 4 ⌋ × 2 + 1 \left\lfloor\dfrac{n}{4}\right\rfloor\times 2+1 ⌊4n⌋×2+1
  • n ≢ 0 ( m o d 4 ) , n ≥ 4 n \not\equiv 0 \pmod{4},n\ge 4 n≡0(mod4),n≥4,输出 ⌊ n 4 ⌋ × 2 + 2 \left\lfloor\dfrac{n}{4}\right\rfloor\times 2+2 ⌊4n⌋×2+2

这也是一个精度问题。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int T;
int n, d;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> T;
	for (int cs = 1; cs <= T; cs++){
		cin >> n >> d;
		if (d == 1){
			if (n == 1){
				cout << 0 << "\n"; 
			}
			else if (n == 2 || n == 3){
				cout << 2 << "\n";
			}
			else if (n % 4 == 0){
				cout << n / 4 * 2 + 1 << "\n";
			}
			else{
				cout << n / 4 * 2 + 2 << "\n";
			}
		}
		else if (d == 2){
			if (n <= 2){
				cout << 0 << "\n";
			}
			else if (n == 3){
				cout << 1 << "\n";
			}
			else if (n == 4){
				cout << 4 << "\n";
			}
			else if (n == 5){
				cout << 3 << "\n";
			}
			else if (n == 6){
				cout << 4 << "\n";
			}
			else if (n == 9){
				cout << 7 << "\n";
			}
			else{
				cout << n - n % 2 << "\n";
			}
		}
		else if (d == 3){
			if (n <= 3){
				cout << 0 << "\n";
			}
			else if (n == 4){
				cout << 1 << "\n";
			}
			else{
				cout << n - 4 << "\n";
			}
		}
		else if (d == 4){
			if (n <= 4){
				cout << 0 << "\n";
			}
			else{
				int r = ((-2.0 + sqrt(4.0 - 4.0 * 2.0 * (1.0 - n))) / 4.0);
				int m = n - 2 * r * r - 2 * r - 1;
				cout << 2 * r * r - 2 * r + 1 + max(0ll, m - m / (r + 1) - 1) << "\n";
			}
		}
	}	
}

AT_abc348_g ABC348G Max (Sum - Max)

原题链接:ABC348G Max (Sum - Max)

分析

这个看着挺有意思的,但是我不会......可能我会 O ( n 2 ) O(n^2) O(n2) 甚至更大的做法?就是我们可能需要一个 DP,甚至是反悔贪心呢?总之这个是一个决策......但是,我没看过决策单调性啊😭

这无疑是恐怖的😭

差点以为这题要高精度。


那估计就是 DP 的优化?看看题解吧。


找 D D D 老师终于是理解了。

首先呢,我们先以 B B B 为关键字排序,那么,对于一个 k k k,如果钦定某个 B i B_i Bi 为所选取的最大值,那么,就要从 j ∈ 1 , i j\in1,i j∈1,i 中选取前 k − 1 k-1 k−1 小的 A j A_j Aj,可以用主席树维护这个东西。

然后呢,我们发现,如果每次都枚举 k k k 是超时的,我们发现,随着 k k k 的增加,我们所选择的 B i B_i Bi 是不降的。那么,可以进行分治。我们设 k ∈ l , r , i ∈ L , R k\inl,r,i\inL,R k∈l,r,i∈L,R,然后,在 L , R L,R L,R 内暴力枚举最适合的位置,即可继续递归下去(这样可以把每次最优位置下传)。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
const int VL = -1000000000;
const int VR =  1000000000;
int n;
struct node{
	int a, b;
}inp[N];
bool cmp(node tmp1, node tmp2){
	return tmp1.b < tmp2.b;
}
int rt[N], tot;
struct tree{
	int ls, rs;
	int cnt, val;
}tr[N << 6];
int ans[N];
struct president_segtree{
	void pushup(int p){
		tr[p].cnt = tr[tr[p].ls].cnt + tr[tr[p].rs].cnt;
		tr[p].val = tr[tr[p].ls].val + tr[tr[p].rs].val;
	}
	void modify(int pre, int &p, int l, int r, int val){
		if (!p)
			p = ++tot;
		if (l == r){
			tr[p].val = tr[pre].val + val;
			tr[p].cnt = tr[pre].cnt + 1;
			return ;
		}
		int mid = (l + r) >> 1;
		if (val <= mid){
			tr[p].rs = tr[pre].rs;
			modify(tr[pre].ls, tr[p].ls, l, mid, val);
		}
		else{
			tr[p].ls = tr[pre].ls;
			modify(tr[pre].rs, tr[p].rs, mid + 1, r, val);
		}
		pushup(p);
	}
	int query(int p, int l, int r, int k){
		if (!k)
			return 0;
		if (l == r){
			return k * l;
		}
		int mid = (l + r) >> 1;
		if (k <= tr[tr[p].rs].cnt)
			return query(tr[p].rs, mid + 1, r, k);
		else
			return tr[tr[p].rs].val + query(tr[p].ls, l, mid, k - tr[tr[p].rs].cnt);
	}
}T;
void solve(int l, int r, int L, int R){
	if (l > r)
		return ;
	int mid = (l + r) >> 1;
	int pos = max(L, mid);
	ans[mid] = 0xc0c0c0c0c0c0c0c0;
	for (int i = max(L, mid); i <= R; i++){
		int tmp = T.query(rt[i], VL, VR, mid);
		if (tmp - inp[i].b > ans[mid]){
			ans[mid] = tmp - inp[i].b;
			pos = i;
		}
	}
	solve(l, mid - 1, L, pos);
	solve(mid + 1, r, pos, R);
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++){
		cin >> inp[i].a >> inp[i].b;
	}
	sort(inp + 1, inp + n + 1, cmp);
	for (int i = 1; i <= n; i++){
		T.modify(rt[i - 1], rt[i], VL, VR, inp[i].a);
	}
	solve(1, n, 1, n);
	for (int i = 1; i <= n; i++){
		cout << ans[i] << "\n";
	}
}

QOJ4998 Keyboard Queries

原题链接:Keyboard Queries

分析

想到了一个什么 segtree+union 的做法,但是,我不会判断,而且,那个 2 1 3 我不会更新。


遇事不决,我们做个离线怎么样啊?我还是感觉线段树优化并查集是对的。


竟然是首先,我们使用并查集来维护(这个想到了),然后,为了判相等,我们需要使用哈希。然后,每次加进来,二分出来第一个不在同一个块内的,使用树状数组单点加,向暴力枚举数量少的一端启发式合并。做完了......

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
#define base 13331
using namespace std;
const int N = 100005;
int n, q;
int pw[N], inv[N];
int fa[N];
vector<int> e[N];
int qpow(int a, int b){
    int res = 1;
    while (b) {
        if (b & 1) 
			res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
struct BIT{
    int c[N];
    BIT(){
        memset(c, 0, sizeof(c));
    }
    void add(int x, int val){
        for (int i = x; i <= n; i += i & (-i)){
            c[i] = (c[i] + val) % mod;
        }
    }
    int query(int x){
        int res = 0;
        for (int i = x; i; i -= i & (-i)){
            res = (res + c[i]) % mod;
        }
        return res;
    }
}T1, T2;
void modify(int x, int y){
    T1.add(x, y * pw[x] % mod);
    T2.add(n - x + 1, y * pw[n - x + 1] % mod);
}
int find(int x){
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int u, int v){
    int fu = find(u), fv = find(v);
    if (fu == fv) 
		return ;
    if (e[fu].size() < e[fv].size()) 
		swap(fu, fv);
    for (auto to : e[fv]){
        fa[find(to)] = fu;
        modify(to, (fu - fv + mod) % mod);
        e[fu].push_back(to);
    }
}
void solve(int l, int r){
    if (l > r) 
		return ;
    int L = 1, R = r - l + 1, pos = n + 1;
    while (L <= R){
        int mid = (L + R) >> 1;
        int tmp1 = (T1.query(l + mid - 1) - T1.query(l - 1) + mod) % mod * inv[l] % mod;
        int tmp2 = (T2.query(n - r + mid) - T2.query(n - r) + mod) % mod * inv[n - r + 1] % mod;
        if (tmp1 != tmp2){
            pos = mid;
            R = mid - 1;
        } 
		else{
            L = mid + 1;
        }
    }
    if (l + pos - 1 >= r - pos + 1) 
		return ;
    merge(l + pos - 1, r - pos + 1);
    solve(l + pos, r - pos);
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> q;
    pw[0] = 1;
    inv[0] = 1;
    for (int i = 1; i <= n; i++){
        pw[i] = pw[i - 1] * base % mod;
        inv[i] = qpow(pw[i], mod - 2);
    }
    for (int i = 1; i <= n; i++){
        fa[i] = i;
        e[i].push_back(i);
        modify(i, i);
    }
    for (int cs = 1, op, l, r, a, b, x, y; cs <= q; cs++){
        cin >> op;
        if (op == 1){
            cin >> l >> r;
            solve(l, r);
        } 
		else if (op == 2){
            cin >> a >> b >> x >> y;
            if (b - a != y - x){
                cout << "Not equal" << "\n";
            } 
			else{
                int tmp1 = (T1.query(b) - T1.query(a - 1) + mod) % mod * inv[a] % mod;
                int tmp2 = (T1.query(y) - T1.query(x - 1) + mod) % mod * inv[x] % mod;
                if (tmp1 == tmp2){
                    cout << "Equal" << "\n";
                } 
				else{
                    cout << "Unknown" << "\n";
                }
            }
        }
    }
    return 0;
}

/*
6 8
1 1 6
2 1 1 6 6
2 1 2 5 6
2 1 3 5 6
1 1 3
2 1 3 4 6
2 4 4 6 6
2 2 3 4 5
*/
相关推荐
夏虫不可与之言冰1 小时前
第三课 点亮 LED 与定时器非阻塞闪烁
c语言·c++·vscode·单片机·嵌入式硬件·物联网
辛苦才能1 小时前
C++ 哈希表深度解析:删除为什么必须打标记,负载因子为什么卡 0.7
开发语言·数据结构·c++·算法
m0_639906941 小时前
HashMap:哈希扰动、扩容源码、红黑树转换、并发问题
java·算法·哈希算法
miller-tsunami2 小时前
STL简介&string相关知识点
开发语言·c++·string
坤坤子吖2 小时前
Linux 线程同步与互斥——互斥锁
linux·服务器·c++·笔记·学习
TDTC_XQ2 小时前
Linux网络编程accept的讲解
linux·服务器·c++
开开心心就好3 小时前
白天晚上自动调蓝光,护眼工具亮度可调
服务器·开发语言·算法·pdf·ocr·intellij-idea·idea
FlightYe3 小时前
音视频修炼之容器与协议(一):MP4和MOV的box剖析
android·linux·c++·ffmpeg·音视频
音草3 小时前
AVL 树详解:从概念到 C++ 实现
c++