2024.11.26 NOIP 模拟赛 题解

T1 珠子(bead)

题意

给定 \(a_{1\sim n}\;(1\le a_i\le m)\),求出 \(\large{\sum_{S\subseteq \{a\}\land S\ne \emptyset}\left(\gcd_{u\in S}u\right)}\) 取模,\(a,n\le10^5\)

分析

方法 1

\[\large{f_x=\left|\left\{S\mid S\subseteq \{a\}\land S\ne \emptyset\land \left(\gcd_{u\in S}u\right)\equiv 0\pmod x\right\}\right|} \]

\[\large{f_x=2^{\sum_{i=1}^n a_i=x}-1} \]

\[\large{g_x=\left|\left\{S\mid S\subseteq \{a\}\land S\ne \emptyset\land \left(\gcd_{u\in S}u\right)=x\right\}\right|} \]

\[\large{g_x=f_x-\sum_{i=2}^{\lfloor\frac mx\rfloor}g_{xi}} \]

答案为

\[\large{\sum_{i=1}^m i\times g_i} \]

时间复杂度 \(O(n+m\log m)\)

代码:

#include <bits/stdc++.h>
#define M 998244353
using namespace std;
int sub(int x, int y){x -= y;return x < 0? x + M : x;}
void smul(int &x, int y){x = static_cast<long long>(x) * y % M;}
int mul(int x, int y){smul(x, y);return x;}void ssub(int &x, int y){x = sub(x, y);}
void sadd(int &x, int y){x += y;if (x >= M)x -= M;}
int fpw(int a, int b){int ret = 1;for (; b; b >>= 1, smul(a, a))if (b & 1)smul(ret, a);return ret;}
int n, m, x, rs, ct[100010], f[100010];//f_i:i|gcd cnt
int main(){
    freopen("bead.in", "r", stdin);freopen("bead.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m;while (n--)cin >> x, ++ct[x];
    for (int i = 1; i <= m; ++i){int Cnt = 0;for (int j = i; j <= m; j += i)Cnt += ct[j];f[i] = sub(fpw(2, Cnt), 1);}
    for (int i = m; i; --i){for (int j = i + i; j <= m; j += i)ssub(f[i], f[j]);sadd(rs, mul(i, f[i]));}
    cout << rs << endl; return 0;
}
/*
3 4
2 4 3

14


10 2
1 1 1 1 1 2 2 2 2 2
1054
*/

方法 2

有恒等式 \(\sum_{d\mid n}\varphi(d)=n\)

因此答案为

\[\large{\begin{aligned} &\sum_{i=1}^m i g_i\\ =&\sum_{i=1}^m\sum_{d\mid i}\varphi(d) g_i\\ =&\sum_{d=1}^m\varphi(d)\sum_{d\mid i} g_i\\ =&\sum_{d=1}^m\varphi(d)f_d\\ \end{aligned}}\]

显然 \(\varphi(1\sim m)\) 可以线性筛 \(O(m)\) 求出

总时间复杂度 \(O(n+m)\)

T2 机器人(robot)HDU 6232 Confliction

题意

有两个分段函数 \(F\)\(G\),定义域为 \(1\sim L\),定义域内取值连续,\(F\) 分为 \(n\) 段,从左到右第 \(i\) 段长度为 \(a_i\),斜率为 \(v_i\)\(G\) 分为 \(m\) 段,第 \(i\) 段长为 \(b_i\),斜率为 \(w_i\),满足 \(\sum a=\sum b=L,\;a_i,b_i\in \mathbb N^+,\;v_i,w_i\in\{-1,0,1\}\),求出 \(\max_{t\in\mathbb Z}\left(\sum_{x=1}^L [F(x)+t=G(x)]\right)\)\(a_i,b_i\le 10^9\)\(n,m\le10^5\),多测 \(\sum(n+m)\le 2\times10^6\)

分析

\(rs(k)\)\(t=k\)\(\sum_{x=1}^L [F(x)+t=G(x)]\) 的值,其中 \(k\in\mathbb Z\),则答案为 \(rs\) 的最大值

枚举 \(i,j\),满足 \(F\) 的第 \(i\) 段(含左端点不含右端点,\(F(1)\) 特判,下同)和 \(G\) 的第 \(j\) 段有交

显然这样的 \((i,j)\) 数量为 \(O(n+m)\) 的,可以通过双指针或二分等方式 \(O(n+m)\)\(O((n+m)\log (n+m))\) 求出所有的 \((i,j)\)

考虑一组 \((i,j)\)\(rs\) 的影响

\(F\) 的第 \(i\) 段为 \((l,r]\),斜率为 \(v\)\(F(l)-F(1)=ofa\)\(G\) 的第 \(j\) 段为 \((L,R]\),斜率为 \(w\)\(G(L)-G(1)=ofb\)

\(v=w\),则会令 \(rs(ofa-ofb-v(l-L))\) 加上 \(\min(r,R)-\max(l,L)\)

否则会令 \(rs(s)\) 加一,其中 \(s\in\{d\mid \exists \max(l,L)<p\le \min(r,R),\; -(p-l)v-ofa+d+ofb+(p-L)w \}\)

可以简化为 \(s=p(v-w)+ofa-ofb-lv+Lw,\max(l,L)<p\le \min(r,R)\)

\(B=ofa-ofb-lv+Lw\)\(K=v-w\)\(Ls=\max(l,L)+1\)\(Rs=\min(r,R)\)

则相当于令 \(rs(Kp+B)\;(Ls\le p\le Rs)\) 加一

显然 \(K\) 的取值为 \(\pm 1,\pm 2\)\(=0\) 的情况之前已经讨论了)

\(K=\pm 1\) 时相当于令 \(rs(K\cdot Ls+B\sim K\cdot Rs+B)\) 加一

\(K=\pm 2\) 时相当于令 \(rs\) 区间 \([K\cdot Ls+B,K\cdot Rs+B]\) 中奇偶性和 \(B\) 相同的位置加一

因此问题转化为单点加,区间加一,区间奇数位置加一,区间偶数位置加一,所有操作完毕后查询全局最大

显然可以离散化后差分计算

时间复杂度 \(O(\sum (n+m)\log (n+m))\),若使用双指针且离散化时的排序改成基排,则可以做到大常数的 \(O(\sum(n+m))\)

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n, m, v[100010], w[100010];
ll a[100010], b[100010], sa[100010], sb[100010], oa[100010], ob[100010];//sum,offset
vector<tuple<ll, ll, ll, ll> > V;vector<ll> P;
void incrngsm(ll L, ll R, int s)
{if (L > R)swap(L, R);if (s)V.emplace_back(L, R, 0, 1);else V.emplace_back(L, R, 1, 0);P.emplace_back(R); P.emplace_back(L - 1);}
void incrng(ll L, ll R){if (L > R)swap(L, R);incrngsm(L, R, 0); incrngsm(L, R, 1);}
void addp(ll p, ll v){if (p & 1)V.emplace_back(p, p, 0, v);else V.emplace_back(p, p, v, 0);P.emplace_back(p); P.emplace_back(p - 1);}
ll adv0[400010], adv1[400010];
ll getmxv(){
    sort(P.begin(), P.end());P.erase(unique(P.begin(), P.end()), P.end());
    fill(adv0, adv0 + P.size() + 5, 0);fill(adv1, adv1 + P.size() + 5, 0);
    for (auto i : V){
        ll L = get<0>(i), R = get<1>(i), ad0 = get<2>(i), ad1 = get<3>(i);
        L = lower_bound(P.begin(), P.end(), L - 1) - P.begin() + 1;
        R = lower_bound(P.begin(), P.end(), R) - P.begin();
        adv0[L] += ad0; adv0[R + 1] -= ad0;adv1[L] += ad1; adv1[R + 1] -= ad1;
    }
    for (int i = 1; i < P.size(); ++i)adv0[i] += adv0[i - 1], adv1[i] += adv1[i - 1];
    ll rs = 0;
    for (int i = 1; i < P.size(); ++i){
        ll L = P[i - 1] + 1, R = P[i];
        if (L == R)rs = max(rs, L & 1? adv1[i] : adv0[i]);else rs = max({rs, adv0[i], adv1[i]});
    }
    return rs;
}
void work(){
    cin >> n;for (int i = 1; i <= n; ++i)cin >> v[i] >> a[i], sa[i] = sa[i - 1] + a[i], oa[i] = oa[i - 1] + v[i] * a[i];
    cin >> m;for (int i = 1; i <= m; ++i)cin >> w[i] >> b[i], sb[i] = sb[i - 1] + b[i], ob[i] = ob[i - 1] + w[i] * b[i];
    V.clear();P.clear();P.emplace_back(-5e18);addp(0, 1);
    for (int i = 1; i <= n; ++i){
        int lj = upper_bound(sb + 1, sb + m + 1, sa[i - 1]) - sb, rj = lower_bound(sb + 1, sb + m + 1, sa[i]) - sb;
        for (int j = lj; j <= rj; ++j){
            ll RgL = max(sa[i - 1], sb[j - 1]) + 1, RgR = min(sa[i], sb[j]);if (RgL > RgR)continue;
            int k = v[i] - w[j]; ll b = oa[i - 1] - ob[j - 1] - sa[i - 1] * v[i] + sb[j - 1] * w[j];
            if (!k)addp(b, RgR - RgL + 1);
            else if ((k & 1) == 1)incrng(k * RgL + b, k * RgR + b);
            else incrngsm(k * RgL + b, k * RgR + b, b & 1);
        }
    }
    cout << getmxv() << '\n';
}
int main(){
    freopen("robot.in", "r", stdin);freopen("robot.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t; cin >> t; while (t--)work();//clog << clock() << "ms" << endl;
    return 0;
}
/*
2
1
1 2
2
1 1
-1 1
1
0 6
4
-1 2
1 1
-1 2
1 1

2
3


1
4
1 4
-1 7
0 6
1 25
39
1 1
0 1
1 1
1 1
0 1
0 1
0 1
1 1
0 1
0 1
0 1
0 1
0 2
1 2
0 1
0 1
1 1
1 1
1 1
1 1
0 1
1 1
0 1
1 1
0 1
1 1
0 1
0 1
1 1
0 1
1 1
1 1
1 1
1 1
1 1
0 1
0 2
0 1
0 1

7
*/

还有一种理解方式是令 \(H(i)=F(i)-G(i)\),显然 \(H\) 为分为 \(O(n+m)\) 段的分段函数,斜率取值为 \(\{0,\pm1,\pm 2\}\),要求 \(H(1\sim L)\) 的众数出现次数

实现基本相同

T3 树(tree)

题意

给定一棵 \(n\) 个点的树,每个点有点权 \(a_i\)。将其划分为若干集合 \(V_1,V_2\cdots\),其中对于每个 \(V_i\),都满足 \(\exists p\in V_i,\forall(u\in V_i,v\in V_i,u\ne v),\operatorname{lca}(u,v)=p\)。这种划分的权值为每个 \(V\) 中权值和的积。求所有合法划分方式的权值之和取模 \(m\)\(n\le 100,0\le a_i,m\le 10^9\)

分析

一个集合 \(S\) 合法等价于存在一个 \(p\),满足所有 \(u\in S\land u\ne p\) 各种属于 \(p\) 的不同子树,以下称这个 \(p\)\(S\) 的关键点

一种划分 \(S=\{V_i\}\) 的权值为 \(\large{\prod_{S'\in S}\sum_{u\in S'}a_u}\)

这等价于 \(\large{\sum_{u_1\in V_1,u_2\in V_2,\cdots,u_k\in V_k}\prod_{i=1}^k a_{u_i}}\)

因此原问题可以转化为将 \(1\sim n\) 划分为若干合法集合,并对每个点黑白染色,满足每个集合中有且仅有一个黑点,权值为所有黑点的权值之积,求所有可能的方案是权值之和

考虑树形 \(dp\)

每次确定一个子树中点的颜色,但其中一部分点没有确定在划分中归属的集合

\(f_{u,i,j}\) 表示 子树 \(u\) 中还有 \(i\) 个黑点和 \(j\) 个白点没有确定集合 的权值和(权值和包括未确定归属的部分,下同),则答案为 \(f_{1,0,0}\)

显然 \(f\) 的值有两种种来源:节点 \(u\) 和当前处理子树之外的某些点同属一个集合;节点 \(u\) 作为一个集合的关键点,与其一些儿子一起组成一个集合

后者又可以根据 \(u\) 的颜色分为两类

\(h_{u,i,j}\)节点 \(u\) 和当前处理子树之外的某些点同属一个集合的贡献

计算过程中\(f\)节点 \(u\) 作为一个集合的关键点,与其一些儿子(可能没有)一起组成一个集合,集合内恰有一个黑点的总贡献,最后再加上 \(h\) 即为前文定义的值

\(g_{u,i,j}\)节点 \(u\) 作为一个集合的关键点,与其一些儿子(可能没有)一起组成一个集合,且这个集合内没有黑点的总贡献(\(g\) 的作用仅仅是辅助求出 \(f\),因此不用考虑这里的集合不合法)

对于一个叶子,有 \(f_{u,0,0}=a_u\)\(u\) 自成一个集合,且 \(u\) 一定染为黑色,否则不合法),\(g_{u,0,0}=1\)\(u\) 自成一个集合,且 \(u\) 染为白色),\(h_{u,0,1}=1\)\(u\) 未确定归属,染为白色),\(h_{u,1,0}=a_u\)\(u\) 未确定归属,染为黑色)

考虑每次加入一个子树

以下记 \(f,g,h\)节点 \(u\) 和当前考虑子树之前的所有子树 部分(以下简称前面部分)的答案,\(F\) 为加入的子树(以下简称当前部分)的 \(f\)\(f',g',h'\) 为加入子树后的 \(f,g,h\)\(f,g,h,F,f',g',h'\) 都是二维数组,以下要根据前四个求出后三个)

在当前部分中,只有三种选择:不选择新的,选择一个新的白色节点和之前的组成新的集合,选择一个新的黑色节点和之前的组成新的集合

第一种选择的转移为

\[f'_{i,j}=\sum_{a+b=i}\sum_{c+d=j} f_{a,c}F_{b,d} \]

\[g'_{i,j}=\sum_{a+b=i}\sum_{c+d=j} g_{a,c}F_{b,d} \]

\[h'_{i,j}=\sum_{a+b=i}\sum_{c+d=j} h_{a,c}F_{b,d} \]

即直接拼接

第二种选择转移为

\[f'_{i,j}=\sum_{a+b=i}\sum_{c+d-1=j}d\;f_{a,c}F_{b,d} \]

\[g'_{i,j}=\sum_{a+b=i}\sum_{c+d-1=j}d\;g_{a,c}F_{b,d} \]

其中 \(d\) 为后面部分中选择的白色节点的方案数

第三种选择转移为

\[f'_{i,j}=\sum_{a+b-1=i}\sum_{c+d=j}b\;g_{a,c}F_{b,d} \]

其中 \(b\) 为后面部分中选择的黑色节点的方案数,\(g_{a,c}\) 为前面部分全选白色节点的方案数(因为有且仅有一个黑色节点)

可以证明,这样时间复杂度为 \(O(n^4)\),常数约 \(\frac 14\)

实现参考了 喵仔牛奶 的代码:

#include <bits/stdc++.h>
#define copy2d(dst, src) ::std::copy_n(*(src), sizeof(src) / sizeof(**(src)), *(dst))
#define clr2d(dst) ::std::fill_n(*(dst), sizeof(dst) / sizeof(**(dst)), remove_reference_t<decltype(**(dst))>{})
using namespace std;
int n, M, a[110], sz[110];
void sadd(int &x, int y){x += y;if (x >= M)x -= M;}
int mul(int x, int y){return static_cast<int>(static_cast<long long>(x) * y % M);}
int mul(int x, int y, int z){return static_cast<int>(static_cast<long long>(x) * y % M * z % M);}
vector<int> ve[110];
int F[110][110][110];//all  blk,wht
int G[110][110][110];//create new set,current is white,one of the children is black,others are white
int H[110][110][110];//not create new set
#define f F[k]
#define g G[k]
#define h H[k]
#define ff F[t]
int uf[110][110], ug[110][110], uh[110][110];//upd
void dfs(int k, int fa){
    f[0][0] = h[1][0] = a[k];/*only current,color is black*/
    g[0][0] = h[0][1] = 1;/*only current,color is white*/
    sz[k] = 1;
    for (int t : ve[k])if (t ^ fa){dfs(t, k);
        for (int b = 0; b <= sz[k]; ++b)for (int w = 0; b + w <= sz[k]; ++w)//w of pre to white,b of pre to black
            for (int B = 0; B <= sz[t]; ++B)for (int W = 0; B + W <= sz[t]; ++W){//W of now to white,B of now to black
                sadd(uf[b + B][w + W], mul(f[b][w], ff[B][W])),
                sadd(ug[b + B][w + W], mul(g[b][w], ff[B][W])),
                sadd(uh[b + B][w + W], mul(h[b][w], ff[B][W]));//mrg with pre,no new chosen points
                if (W)//consume one white point from subtree(t)
                    sadd(uf[b + B][w + W - 1], mul(f[b][w], ff[B][W], W)),
                    sadd(ug[b + B][w + W - 1], mul(g[b][w], ff[B][W], W));/*W choices of the chosen white point*/
                if (B)//consume one black point from subtree(t)
                    sadd(uf[b + B - 1][w + W], mul(g[b][w], ff[B][W], B));/*B choices of the chosen black point*/
            }
        copy2d(f, uf);copy2d(g, ug);copy2d(h, uh);clr2d(uf);clr2d(ug);clr2d(uh);sz[k] += sz[t];
    }
    for (int i = 0; i <= sz[k]; ++i)for (int j = 0; i + j <= sz[k]; ++j)sadd(f[i][j], h[i][j]);
}
int main(){
    freopen("tree.in", "r", stdin);freopen("tree.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> M;
    for (int i = 1; i <= n; ++i)cin >> a[i];
    for (int i = 1, u, v; i < n; ++i){cin >> u >> v;ve[u].emplace_back(v);ve[v].emplace_back(u);}
    dfs(1, 0); cout << F[1][0][0] << endl;
    return 0;
}
/*
3 998244353
1 2 3
1 2
1 3

29
*/

T4 钻石岛(island)

题意

给定 \(a,b,c,x,y\),有一个 \(a\times b\times c\) 的立方体,初始在 \((0,0)\)(立方体的位置定义为其底面 \(x\)\(y\) 坐标最小的一点的坐标)且长为 \(a\) 的棱与 \(x\) 轴平行,长为 \(b\) 的棱和 \(y\) 轴平行,\(c\) 为高,要将其移到 \((x,y)\),且长宽高不变,求最少翻转的步数,\(a,b,c\le1000,|x|,|y|\le10^{18}\)

分析

若当前在 \((p,q)\)\(x\) 轴方向长度为 \(a\)\(y\) 轴方向长度为 \(b\),高度为 \(c\)

显然可以用 \(2(v+w)\) 次操作移到所有位置 \((p\pm v(a+c),q\pm w(b+c))\),定义这样的连续若干次操作为一次平移

可以证明,一定存在一种最优解,使得除了平移外最多只有 \(8\)

因此暴力搜索前 \(8\)

设走到 \((p,q)\)\(x\) 轴方向长度为 \(a\)\(y\) 轴方向长度为 \(b\),高度为 \(c\),已经走了 \(dep\;(\le 8)\)

显然 \(x\) 方向上的翻转和 \(y\) 方向上的翻转可以分开考虑

问题转化为给定 \(n,p,q,r\),求 \(px+qy+rz=n\) 的一组整数解,最小化 \(|x|+|y|+|z|\),其中 \(\{p,q,r\}=\{a+b,a+c,b+c\}\)

若在暴力搜索的过程中没有用到某个长度(例如搜索过程中一直向前翻转,则在 \(y\) 轴方向只能使用 \(b+c\)\(a+c\),此时需要强制令 \(a+b\) 一项系数为 \(0\)

因此需要修改为:给定 \(n,p,q,r\),求 \(px+qy+rz=n\) 的一组整数解,最小化 \(|x|+|y|+|z|\),其中 \(\{p,q,r\}=\{a+b,a+c,b+c,-1\}\),若 \(p,q,r\) 中有 \(-1\),则表示其对应系数必须为 \(0\)(这只要在 \(bfs\) 过程中禁用该长度扩展即可)

假定 \(r\) 为三个数中最大的

则对于每个 \(s\in [0,r)\),保存若干二元组 \((t,n)\),表示从 \(0\)\(t\) 只要 \(n\) 步,其中 \(t\equiv s\pmod r\)

这样计算到 \(m\) 距离时只要暴力枚举这些二元组并取 \(\frac{|m-t|}r+n\) 最小即可

这可以 \(bfs\) 实现

可以证明最优情况下每个 \(s\) 保存的二元组数量不超过 \(O(r)\)

同时 \(\{p,q,r\}\) 显然只有 \(O(1)\) 种,需要对相同的进行记忆化

总时间复杂度 \(O(\max(a,b,c)^2)\),常数大,但时间复杂度跑不满

实现参考了题解:

#include <bits/stdc++.h>
using namespace std;
int main(){
    freopen("island.in", "r", stdin);freopen("island.out", "w", stdout);
    long long X, Y, rs = 5e18;
    int A, B, C, xct[2005]{}, yct[2005]{};
    map<array<int, 3>, vector<vector<pair<long long, int> > > > mem;
    function<void(long long, long long, int, int, int, int)> dfs = [&](long long x, long long y, int dep, int a, int b, int c){
        ++xct[a + c]; ++yct[b + c];
        auto Sl = [&mem](long long x, int a, int b, int c){
            //x=pa+qb+rc,min |p|+|q|+|r|,if a=-1 then p=0,if b=-1 then q=0,if c=-1 then c=-1
            tie(a, b, c) = make_tuple(min({a, b, c}), a + b + c - min({a, b, c}) - max({a, b, c}), max({a, b, c}));//a<=b<=c
            auto &rf = mem[{a, b, c}]; auto Md = [c](long long v){v %= c;return v < 0? v + c : v;};
            auto dis = [&](long long x){
                auto abs = [](long long v){return v < 0? -v : v;};
                long long ret = 1e18;for (auto i : rf[Md(x)])ret = min(ret, abs(x - i.first) / c + i.second);return ret;
            };
            if (rf.empty()){//bfs
                rf.resize(c);queue<pair<long long, int> > Q;Q.emplace(0, 0);
                while (!Q.empty()){
                    auto t = Q.front();Q.pop();long long n = t.first; int stp = t.second;
                    if (dis(n) > stp){
                        rf[Md(n)].emplace_back(t);
                        for (int t : {a, b, c})if (~t)Q.emplace(n - t, stp + 1), Q.emplace(n + t, stp + 1);
                    }
                }
            }
            //clog << x << " " << a << " " << b << " " << c << ":" << dis(x) << endl;
            return dis(x) * 2/*each operator -> 2 turn*/;
        };
        #define SlArg(ct) ct[a + b]? a + b : -1, ct[a + c]? a + c : -1, ct[b + c]? b + c : -1
        if (a == A && b == B && c == C)rs = min(rs, dep + Sl(X - x, SlArg(xct)) + Sl(Y - y, SlArg(yct))/*splt x and y*/);
        if (dep < 8){
            dfs(x + a, y, dep + 1, c, b, a);/*front*/dfs(x - c, y, dep + 1, c, b, a);/*back*/
            dfs(x, y + b, dep + 1, a, c, b);/*left*/ dfs(x, y - c, dep + 1, a, c, b);/*right*/
        }
        --xct[a + c]; --yct[b + c];
    };
    cin >> A >> B >> C >> X >> Y;dfs(0, 0, 0, A, B, C);
    if (rs >= 2e18)cout << "impossible" << endl;else cout << rs << endl;
    return 0;
}
/*
1 1 2 9 0    6
3 4 5 8 0    2
3 4 5 -8 9    4
3 4 5 123 45   40
2 33 1 -23 42    18
20 10 30 13 37    impossible
95 94 76 -10 -4    52
951 951 165 -373721873115219 -157139719882902    558214083389
*/

比赛结果

\(0+100+0+20\)\(T1\) 由于样例(包括自己造的)过小,没有找出错误,丢 \(100\) 分;\(T4\) 预计 \(35\),有一个数据包出错,丢 \(15\) 分;其余符合预计

posted @ 2024-11-27 07:19  Hstry  阅读(24)  评论(0)    收藏  举报