2024.11.19 NOIP 模拟赛 题解

T1 战队分配(team)

题意

给定 \(a_{i,j}\;(1\le i,j\le n)\),将 \(\{1,2,\cdots,n\}\) 划分为两个相同大小的子集 \(S_1,S_2\)(保证 \(n\) 为偶数),使 \(\vert f(S_1)-f(S_2)\vert\) 最大,其中 \(f(S)=\sum_{u\in S,v\in S,u<v}a_{u,v}\),求这个最大值,\(n\le1000\),保证 \(a_{i,j}=a_{j,i},a_{i,i}=0\)

分析

\(s_i=\sum_{j=1}^n a_{i,j}\),则

\[\begin{aligned} &f(S_1)-f(S_2)\\ =&\sum_{u\in S_1,v\in S_1,u<v}a_{u,v}-\sum_{u\in S_2,v\in S_2,u<v}a_{u,v}\\ =&\frac12\left(\sum_{u\in S_1,v\in S_1}a_{u,v}-\sum_{u\in S_2,v\in S_2}a_{u,v}\right)\\ =&\frac12\left(\left(\sum_{u\in S_1,v\in S_1}a_{u,v}+\sum_{u\in S_1,v\in S_2}a_{u,v}\right)-\left(\sum_{u\in S_2,v\in S_2}a_{u,v}+\sum_{u\in S_1,v\in S_2}a_{u,v}\right)\right)\\ =&\frac12\left(\left(\sum_{u\in S_1,v\in S_1}a_{u,v}+\sum_{u\in S_1,v\in S_2}a_{u,v}\right)-\left(\sum_{u\in S_2,v\in S_2}a_{u,v}+\sum_{u\in S_2,v\in S_1}a_{u,v}\right)\right)\\ =&\frac12\left(\left(\sum_{u\in S_1,v\in S_1\cup S_2}a_{u,v}\right)-\left(\sum_{u\in S_2,v\in S_1\cup S_2}a_{u,v}\right)\right)\\ =&\frac12\left(\sum_{u\in S_1}s_u-\sum_{u\in S_2}s_u\right)\\ \end{aligned}\]

由于要使其绝对值最大,即 \(\sum_{u\in S_1}s_u\)\(\sum_{u\in S_2}s_u\) 相差最大,因此将最大的 \(\frac n2\)\(s\) 放入 \(S_1\),剩余放入 \(S_2\) 一定最优

时间复杂度 \(O(n^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
int n, a[1010][1010];
struct node{long long Sm;int id;} N[1010];
bool Mk[1010];
int main(){
    freopen("team.in", "r", stdin);freopen("team.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; N[i].id = i, ++i)
        for (int j = 1; j <= n; ++j)cin >> a[i][j], N[i].Sm += a[i][j];
    sort(N + 1, N + n + 1, [](node a, node b){return a.Sm < b.Sm;});
    for (int i = 1; i <= n >> 1; ++i)Mk[N[i].id] = 1;
    long long C[2]{};
    for (int i = 1; i <= n; ++i)
        for (int j = i + 1; j <= n; ++j)
            if (Mk[i] == Mk[j])C[Mk[i]] += a[i][j];
    cout << C[0] - C[1] << endl;
    return 0;
}
/*
6
0 4 -6 2 3 -3
4 0 2 -6 0 0
-6 2 0 0 2 2
2 -6 0 0 -1 5
3 0 2 -1 0 -4
-3 0 2 5 -4 0

0


4
0 1 2 2
1 0 8 -3
2 8 0 5
2 -3 5 0

6
*/

T2 货车运输(cargo)

题意

给定 \(n,k\),求 \(\sum_p\sum_q[\sum_{i=1}^n\max(p_i,q_i)=k]\),其中 \(p\)\(q\) 为长度 \(n\) 的排列,\(n\le100,k\le n^2\)

分析

类似 [ABC134F] Permutation Oddness

考虑从小到大将 \(1\sim n\) 依次填入 \(p\)\(q\)(每次将一个数同时填入两个排列)

定义 \((p_i,q_i)\) 为一组,定义一组填完为两个数都填了

\(f_{i,x,s}\)\(1\sim i\) 填入 \(p\)\(q\),其中填完了 \(x\) 组,且这 \(x\) 组较大值的总和为 \(s\),有 \(i-x\) 组只填了 \(p\)\(i-x\) 组只填了 \(q\)\(n-i-2(i-x)\) 组完全没有填 的方案数

显然 \(f_{0,0,0}=1\),最终答案为 \(f_{n,n,m}\)

考虑从 \(f_{i-1}\) 转移到 \(f_i\)

\(yz=i-1-x\),表示 填了 \(1\sim i-1\) 时,只填了 \(p\)(或 \(q\),显然两者数量相同)的数量

\(Rs=n-i-2yz\),表示 填了 \(1\sim i-1\) 时,完全没有填的组的数量

有四种转移:

  1. 两个 \(i\) 填在 \(p\)\(q\) 的同一下标,则新增一个填满的组,其位置有 \(Rs\) 种选择,转移为 \(Rs\cdot f_{i-1,x,s}\rightarrow f_{i,x+1,s+i}\)
  2. 将一个 \(i\) 与一个只填了 \(p\) 的配对,另一个新增一组只填 \(q\) 的;或一个 \(i\) 与一个只填了 \(q\) 的配对,另一个新增一组只填 \(p\) 的。两种情况的方案数都是 \(yz\cdot Rs\),转移为 \(2\cdot yz\cdot Rs\cdot f_{i-1,x,s}\rightarrow f_{i,x+1,s+i}\)(可以与上一种合并)
  3. 一个 \(i\) 与一个只填了 \(p\) 的配对,另一个与一个只填了 \(q\) 的配对,新增两个完整的组,且权值都是 \(i+1\),有 \(yz^2\) 种选择,转移为 \(yz^2 \cdot f_{i-1,x,s}\rightarrow f_{i,x+2,s+2i}\)
  4. 一个 \(i\) 新增一组只填 \(q\) 的,另一个新增一组只填 \(p\) 的,方案数为 \(C_{Rs}^2\),转移为 \(C_{Rs}^2f_{i-1,x,s}\rightarrow f_{i,x,s}\)

时间复杂度 \(O(n^2k)\),空间复杂度 \(O(nk)\),需要滚动数组优化

代码:

#include <bits/stdc++.h>
#define M 1000000007
using namespace std;
int add(int x, int y){x += y;return x >= M? x - M : x;}void sadd(int &x, int y){x += y;if (x >= M)x -= M;}
int mul(int x, int y){return static_cast<long long>(x) * y % M;}
int n, m, f[2][110][10010];
int main(){
    freopen("cargo.in", "r", stdin);freopen("cargo.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m; f[0][0][0] = 1;
    for (int i = 1, p = 1, q = 0; i <= n; ++i, swap(p, q)){
        fill(*(f[p]), *(f[p]) + 110 * 10010, 0);
        for (int x = 0; x < i; ++x)
            for (int s = 0; s <= m; ++s){
                int yz = i - x - 1, Rs = n - x - yz - yz;
                if (s + 2 * i <= m)sadd(f[p][x + 2][s + 2 * i], mul(f[q][x][s], mul(yz, yz)));
                if (s + i <= m)sadd(f[p][x + 1][s + i], mul(f[q][x][s], mul(add(mul(2, yz), 1), Rs)));
                sadd(f[p][x][s], mul(f[q][x][s], mul(Rs, Rs - 1)));
            }
    }
    cout << f[n & 1][n][m] << endl;
    return 0;
}
/*
2 4

2


3 7

12


10 80

254719636


10 75

880121070


9 65

881169913
*/

T3 甜果(sugar)

题意

给定 \(b_{1\sim n}\)\(w_{1\sim n}\),有 \(n\) 个变量 \(x_{1\sim n}\) 初始为 \(a_{1\sim n}\)\(n\) 个操作形如 \(x_i>x_{b_i}\) 则令 \(x_i\leftarrow x_i+w_i\),其中 \(1\le i\le n\),若 \(n\) 个操作顺序完全随机,求操作过后 \(x_{1\sim n}\) 的期望取模,多测 \(t\le5\times10^5,\sum n\le 5\times10^5\)

分析

显然每个 \(x_i\) 最终只有两种取值,\(a_i\)\(a_i+w_i\)

\(x_{b_i}\ge x_i\) 时,显然 \(x_i\) 必取 \(a_i\)

\(x_i>x_{b_i}+w_{b_i}\) 时,显然 \(x_i\) 必取 \(a_i+w_i\)

其他情况则取决于 \(x_{b_i}\) 是否加上 \(w_{b_i}\),即此时 \(x_i\) 最终取值为 \(x_i+w_i\) 当且仅当操作序列中 \(b_i\)\(i\) 之前

处理出每个点 \(i\) 到最近的确定取值点(前两类)的距离 \(L_i\)(定义为路径上经过的点数,包括端点),可以证明一定存在

设这 \(L_i\) 个节点在操作序列中的排名分别为 \(v_{1\sim L_i}\)(将其离散化为 \(1\sim L_i\) 的排列),其中 \(v_1\) 为确定取值点的排名,\(L_i\) 为当前处理点的排名

则一个 \(v\) 序列能使 \(b_i\) 加上 \(w_i\),当且仅当 \(v_{L_i}<v_{L_i-1}>v_{L_i-2}<\cdots\)

可以证明,这样的序列个数为 \(L_i!-D_{L_i}\),其中 \(D\) 为错排序列

时间复杂度 \(O(\sum n)\)

代码:

#include <bits/stdc++.h>
#define M 1000000007
#define Fn for (int i = 1; i <= n; ++i)
using namespace std;
int mul(int x, int y){return static_cast<long long>(x) * y % M;}int add(int x, int y){x += y;return x >= M? x - M : x;}
int sub(int x, int y){x -= y;return x < 0? x + M : x;}void sadd(int &x, int y){x = add(x, y);}void smul(int &x, int y){x = mul(x, y);}
int fpw(int x, int y){int ret = 1;for (; y; y >>= 1, smul(x, x))if (y & 1)smul(ret, x);return ret;}
int fc[500010]{1}, ivfc[500010], D[500010]{0, 0, 1};
int n, a[500010], b[500010], w[500010];
int L[500010];//distance(cnt of vertex,include extreme points) to next one bound to happen
int dfs(int k){if (~L[k])return max(0, L[k]);return L[k] = 1 + dfs(b[k]);}
void work(){
    cin >> n;Fn cin >> a[i];Fn cin >> b[i];Fn cin >> w[i];
    Fn L[i] = a[b[i]] >= a[i]? (int)-1e9 : a[i] > a[b[i]] + w[b[i]]? 1 : -1;Fn L[i] ^ -1 || dfs(i);
    Fn cout << add(a[i], L[i] > 0? mul(mul(sub(fc[L[i]], D[L[i]]), ivfc[L[i]]), w[i]) : 0) << " ";cout << '\n';
}
int main(){
    freopen("sugar.in", "r", stdin);freopen("sugar.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    for (int i = 1; i <= 5e5; ++i)fc[i] = mul(fc[i - 1], i);
    ivfc[500000] = fpw(fc[500000], M - 2);
    for (int i = 499999; ~i; --i)ivfc[i] = mul(ivfc[i + 1], i + 1);
    for (int i = 3; i <= 5e5; ++i)D[i] = mul(i - 1, add(D[i - 1], D[i - 2]));
    int t;cin >> t;while (t--)work();
    return 0;
}
/*
4
4
2 5 5 2
4 2 1 3
3 2 1 4
3
5 4 3
1 1 1
6 6 6
3
5 4 3
2 3 1
1 2 3
5
2 1 3 2 1
5 1 1 3 4
1 3 4 2 4

2 5 6 2
5 4 3
500000009 6 3
3 1 5 2 1
*/

T4 打平就能出线!(qualify)

题意

给定 \(n,m,d,c_{1\sim n},p_{1\sim n},gd_{1\sim n},x_{1\sim s},y_{1\sim s}\)(其中 \(s=\sum_{i=1}^n m-c_i\)),保证 \(m-2\le c_i\le m,p_i\le 3m,(\forall i\ne j,(x_i,y_i)\ne (x_j,y_j)),(\forall 1\le i\le n,c_i+\sum_{j=1}^s([x_j=i]+[y_j=i])=m)\),有序列 \(Sq_{1\sim s}\),对于每个 \(1\le i\le s\):若 \(Sq_i=0\) 则令 \(p_{x_i}\)\(p_{y_i}\)\(1\);若 \(Sq_i<0\),则令 \(p_{y_i}\)\(3\)\(gd_{y_i}\) 加上 \(-Sq_i\);否则令 \(p_{x_i}\)\(3\)\(gd_{x_i}\) 加上 \(Sq_i\)。操作结束后,将所有二元组 \((p_i,gd_i)\) 排序,以 \(p\) 为第一关键字,\(gd\) 为第二关键字,若两者都相同,则顺序任意。求对于所有 \(Sq\)\((p_d,gd_d)\) 最后可能的最大最小排名,\(n,m\le3\times10^5\)

分析

(此题过于变态,\(std\) 长达 \(13k\),赛场上除 \(std\) 外两所学校(联考)只有一人拿了 \(10pts\),其余人都没分,还无法理解题解,暂不订正)

比赛结果

\(20+25+5+0\),近期最惨的一次

posted @ 2024-11-19 19:49  Hstry  阅读(36)  评论(0)    收藏  举报