OIFC 2025.11.21 模拟赛总结

快考 NOIP 了。

T1 构造题

题目描述

给定一个正整数 \(n\),请构造一个长度为 \(n\) 的排列 \(p\),满足 \(\forall i \in [1, n - 1]\)\(p_i \oplus p_{i + 1}\) 不是质数,或报告无解。

数据范围:\(1 \leq \sum n \leq 5 \times 10^6\)\(1 \leq T \leq 10^5\)

题解

如果两个数模 \(4\) 同余,则其二进制最后两位是一样的,因此这两个数异或结果是 \(4\) 的倍数!

因此,我们可以把 \(1 \sim n\) 分成 \(4\) 类,只需考虑如何合并这 \(4\) 类。

先说结论:假设 \(P_r(n)\) 表示 \([1, n]\)\(\bmod 4 = r\) 的数从小到大排序得到的序列,则构造:

\[rev(P_1(n)) + rev(P_3(n)) + P_2(n) + P_0(n) \]

合法。其中 \(rev\) 表示把整个序列翻转,即倒序。

简单证明一下:对于第一个 \(+\),显然 \(1\) 和大奇数异或是大偶数;对于第二个 \(+\),显然 \(3 \oplus 2 = 1\);对于第三个 \(+\),两个偶数异或一定得到偶数,而且当 \(n\) 足够大的时候,异或结果一定大于 \(2\),所以这样构造在 \(n\) 比较大的时候是正确的。

简单尝试一下,可以发现 \(n \geq 10\) 的时候可以直接这样构造。对于 \(n < 10\) 的情况,直接跑 dfs 暴搜即可。

参考代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
inline void write(int x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}
signed main(){
    freopen("constructive.in", "r", stdin);
    freopen("constructive.out", "w", stdout);
    int T = read();
    while(T--){
        int n = read();
        if(n < 10){
            if(n == 1) puts("1");
            if(n == 2) puts("-1");
            if(n == 3) puts("-1");
            if(n == 4) puts("-1");
            if(n == 5) puts("1 5 3 2 4");
            if(n == 6) puts("-1");
            if(n == 7) puts("1 5 3 7 6 2 4");
            if(n == 8) puts("1 5 3 2 4 8 6 7");
            if(n == 9) puts("4 2 6 7 3 5 1 8 9");
            continue;
        }
        for(int i = n; i >= 1; i--) if(i % 4 == 1) write(i), putchar(' ');
        for(int i = n; i >= 1; i--) if(i % 4 == 3) write(i), putchar(' ');
        for(int i = 1; i <= n; i++) if(i % 4 == 2) write(i), putchar(' ');
        for(int i = 1; i <= n; i++) if(i % 4 == 0) write(i), putchar(' ');
        putchar('\n');
    }
    return 0;
}

T2 解方程

题目描述

有长为 \(N\) 的整数数列 \(A = (A_1, A_2, \cdots, A_N)\)但是并未给出数列 \(A\) 的具体值

给定 \(Q\) 个关于整数数列 \(A\) 的事件。第 \(i\)\((1 \leq i \leq Q)\) 事件由 \(4\) 个整数 \(T_i,X_i,Y_i,V_i\) 表示。

  • \(T_i = 0\) 表示给出 \(A_{X_i} + A_{Y_i} = V_i\)

  • \(T_i = 1\) 表示当 \(A_{X_i} = V_i\) 时,询问 \(A_{Y_i}\) 的值。

注意询问之间是相互独立的。

请依次处理事件并回答每个询问。如果询问的时候 \(A_{Y_i}\) 的值无法确定,输出 Ambiguous 回答询问,如果询问的时候没有任何满足所有条件的长为 \(N\) 的整数数列 \(A\),输出 Impossible 回答询问。

本题单个测试点内有多组测试数据,并且可能会要求强制在线。

输入格式

从文件 equation.in 中读入数据。

第一行三个用一个空格分隔的整数 \(C, M, W\) 分别表示测试数据组数、强制在选参数以及 \(V_i\) 的值域。

对于每组测试数据:

第一行一个正整数 \(N\)

第二行一个正整数 \(Q\)

接下来 \(Q\) 行,每行四个用一个空格分隔的整数,其中第 \(i\) 行分别为 \(T_i', X_i', Y_i, V_i'\)

有强制在线参数 \(L\),若本组测试数据尚未产生输出,则 \(L = 0\),否则,若上一次询问输出了 Ambiguous,则 \(L = 123456797654321\),否则,若上一次询问输出了 Impossible,则 \(L = 976543212345679\),否则,记上一次询问输出的数字为 \(l\),则 \(L = l + 515472268187439\),可以证明 \(L\) 总是大于等于 \(0\) 的整数。

在每次询问完成后以及每组测试数据开始前都需要对 \(L\) 进行重新计算。

对于第 \(i\) 个事件:

  • \(T_i = (T_i' + M \times L) \bmod 2\)

  • \(X_i = (X_i' - 1 + M \times L) \bmod N + 1\)

  • \(Y_i = (Y_i' - 1 + M \times L) \bmod N + 1\)

  • \(V_i = (V_i' + M \times L) \bmod (w + 1)\)

输出格式

输出到文件 equation.out 中。

对于每组测试数据:

每组询问输出一行,表示该组询问的答案。如果 \(A_{Y_i}\) 的值无法确定,输出 Ambiguous,如果没有任何满足所有条件的数列 \(A\),输出 Impossible

样例

Input 1

2 0 5
4
5
0 1 2 3
1 1 2 1
0 3 4 0
1 4 2 5
1 4 4 3
5
5
0 1 2 3
0 3 4 1
1 5 5 2
0 1 4 5
1 3 2 3

Output 1

2
Ambiguous
3
2
-4

Input 2

2 1 5
4
5
0 1 2 3
1 1 2 1
1 2 3 1
0 3 1 0
0 3 3 4
5
5
0 1 2 3
0 3 4 1
1 5 5 2
1 5 3 0
0 2 1 4

Output 2

2
Ambiguous
3
2
-4

数据范围

对于所有数据满足:

  • \(1 \leq C \leq 20\)

  • \(M \in \{0, 1\}\)

  • \(0 \leq W \leq 2 \times 10^9\)

  • \(1 \leq N \leq 10^5\)

  • \(1 \leq Q \leq 10^5\)

  • \(T_i' \in \{0, 1\}(1 \leq i \leq Q)\)

  • \(T_i \in \{0, 1\}(1 \leq i \leq Q)\)

  • \(1 \leq X_i', Y_i' \leq N(1 \leq i \leq Q)\)

  • \(1 \leq X_i, Y_i \leq N(1 \leq i \leq Q)\)

  • \(0 \leq V_i' \leq W(1 \leq i \leq Q)\)

  • \(0 \leq V_i \leq W(1 \leq i \leq Q)\)

  • 输入的数字全部为整数。

本题不采用捆绑测试。

题解

考虑使用并查集维护。

考虑记录一个数组,表示是否知道一个点的具体值。如果不知道,则记录其与并查集根节点的差。

我们使用拓展域并查集维护边权信息,另外还需要维护路径的奇偶性。此时加边就可以直接加,查询就可以直接查。

如果出现了合法的奇环,只需将连通块的根附上权值就可以高效地维护了。

参考代码:

#include<bits/stdc++.h>
#define int long long
#define mset(a) memset(a, 0, sizeof(a))
using namespace std;
inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
inline void write(int x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}
int T, M, W, n, q, fa[100005], sz[100005], L, isknown[100005], val[100005], dep[100005];
bool bad = false;
vector<pair<int, int> > e[100005];
const int Ambiguous = 123456797654321ll, Impossible = 976543212345679ll, Normal = 515472268187439ll;
inline int find(int k){
    if(fa[k] == k) return k;
    return fa[k] = find(fa[k]);
}
inline void clear(){
    L = 0;
    n = read(), q = read();
    for(int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1;
    mset(isknown), mset(val);
    bad = false;
    mset(dep);
    for(int i = 0; i < 100005; i++) e[i].clear();
    return;
}
inline void dfs(int u, int fa){
    for(auto p : e[u]){
        int v = p.first, w = p.second;
        if(v == fa) continue;
        if(isknown[u] && isknown[v] && val[u] + val[v] != w) return bad = true, void();
        val[v] = w - val[u], dep[v] = !dep[u];
        if(isknown[u]) isknown[v] = true;
        dfs(v, u);
    }
    return;
}
inline void Solve(){
    clear();
    while(q--){
        int t = read(), x = read(), y = read(), v = read();
        t = (t + M * L) % 2;
        x = (x - 1 + M * L) % n + 1;
        y = (y - 1 + M * L) % n + 1;
        v = (v + M * L) % (W + 1);
        if(t == 0){
            if(isknown[x] && isknown[y] && val[x] + val[y] != v) bad = true;
            if(bad || (isknown[x] && isknown[y])) continue;
            if(find(x) == find(y)){
                if(dep[x] != dep[y]){
                    if(val[x] + val[y] != v){
                        bad = true;
                    }
                }else{
                    if((v + val[x] - val[y]) % 2 == 0 && (v - val[x] + val[y]) % 2 == 0){
                        isknown[x] = isknown[y] = true;
                        int temp1 = (v + val[x] - val[y]) >> 1, temp2 = (v - val[x] + val[y]) >> 1;
                        val[x] = temp1, val[y] = temp2;
                        dfs(x, 0);
                    }else bad = true;
                }
            }else{
                if(sz[find(x)] < sz[find(y)]) swap(x, y);
                int fx = find(x), fy = find(y);
                if(isknown[x]){
                    isknown[y] = true, val[y] = v - val[x], dep[y] = !dep[x], dfs(y, 0);
                    fa[fy] = fx, sz[fx] += sz[fy];
                }else if(isknown[y]){
                    isknown[x] = true, val[x] = v - val[y], dep[x] = !dep[y], dfs(x, 0);
                    fa[fx] = fy, sz[fy] += sz[fx];
                }else{
                    val[y] = v - val[x], dep[y] = !dep[x], dfs(y, 0);
                    fa[fy] = fx, sz[fx] += sz[fy];
                }
                e[x].push_back({y, v}), e[y].push_back({x, v});
            }
        }else{
            int fx = find(x), fy = find(y);
            if(bad || (isknown[x] && val[x] != v)){
                L = Impossible;
                puts("Impossible");
            }else if(fx != fy){
                if(isknown[y]){
                    L = val[y] + Normal;
                    write(val[y]), putchar('\n');
                }else{
                    L = Ambiguous;
                    puts("Ambiguous");
                }
            }else{
                int ans = val[y] + (dep[x] != dep[y] ? -1 : 1) * (v - val[x]);
                if(isknown[y] && val[y] != ans){
                    L = Impossible;
                    puts("Impossible");
                }else{
                    L = ans + Normal;
                    write(ans), putchar('\n');
                }
            }
        }
    }
    return;
}
signed main(){
    freopen("equation.in", "r", stdin);
    freopen("equation.out", "w", stdout);
    T = read(), M = read(), W = read();
    while(T--) Solve();
    return 0;
}
posted @ 2025-11-21 13:37  zhang_kevin  阅读(18)  评论(0)    收藏  举报