省选考试总结(1)

省选集训考试总结

  • 3-17 (出题人: fateice ?(题风很像,但没有落款))

  • 得分情况 : 60+8+44=112

  • 题目:

    • T1 : 容斥

      有一个长度为n的排列a,a的一些位置已经确定了.请求出有多少种方案

      对于所有 1<=i<=n 都满足 ai!=i. 对998244353取模. (1n106)

      这题容斥,我考试时候只想到Θ(n2)的容斥...

      后来发现那个用一个鬼畜的式子能变成Θ(n)...

      其实答案就是i=0k(1)i(ki)(li)!.

      上面k就是就是可以没有在a中出现,并且自己的位置存在的数的个数.

      l就是所有没在a中出现的次数个数.

      那么我们就是可以这样理解咯... (ki)(li)! 就是表示至少有i个数可以对应在原位置上的方案数.

      那我们就是要求有0个数对应在原位置上的方案数 那么直接容斥算就行咯QAQ

      容斥其实就是,一个算的加上去,两个算的减一个,三个算的加回来,然后又减掉.(用了组合数的一些性质吧.)

    • T2 : 点分治 (暂时不填了TAT)

    • T3 : 有意思的鬼畜spj题

      有一个数列生成器,给定正整数n,m,a,b和实数p时,它会生成一个满足以
      下条件的数列:

      1. 数列长度为 n (1n5000);

      2. 对于数列中的每个元素,它有p的概率为arand(),有1p的概率为

        brand(),其中rand()是一个在[1,m]中均匀随机的整数。

      t组数据,每组数据给定正整数n,m以及一个按以上方式生成的数列,你
      需要求出ab的值。保证1<=a<=b<=m. (m109)

      说到这题,我就必须要提一下他鬼畜(坑比)的得分机制 .

      对于一个测试点,若分值为p,你的正确率为q,则你的得分为p2100(1q),
      一个subtask的得分为所有测试点得分的最小值。

      这个意思就是,如果你正确率为99%就是一半的分,如果为95%就基本没分了,并且取一个subtask的最低分..fuck

      但其实题目并不是很难...瞎搞就行了qwq

      这道题可以随机两个数xy然后,假设他们是一个组,我们就能令它们的gcda那么我们就能求出另外一个b.

      然后对于这对(a,b)进行一下check就行了,不行的话就继续随机... 可以玄学证明它正确性很高...

  • 总结:

    今天还行qwq... 跪烂Hany01怒吊T2!!! 以后要多熟练计数题了!!!不要太怕麻烦啊!!!!


  • 3-23 (出题人: h10 )

  • 得分情况 : 30+20+30=70......(倒数了...(暴力分滚粗了TAT))

  • 题目 :

    • T1 : DFS

      给你一颗有 n 个点的树,每个点有个权值.一个点的答案为,其他所有点和它的距离小于等于它到根节点距离的权值和. (即 ans[u]=dis(i,u)<dis(root,u)val[i] ). 问每个点最后的答案. ( n106 )

      我们直接算一个点的答案好像不太好算.... 但我们可以考虑每个点对于哪些点的答案产生了贡献

      即一个点 u 它会对它到根节点上路径的中点的子树产生贡献 其他地方不存在了

    • T2 : 矩阵快速幂

      给定函数 f0 , 定义 fk[i]=(d|ifk1[d])(mod998244353).

      m 组询问,每次给你两个数 xk , 求 fk[x] 的值. ( k105,x105,m103 )

      这道题不难想到一个朴素的dp,考虑在第 i 层中,每个数对于 i1 层哪些数的需要的系数. 不断迭代下去就行了.

      每层最多需要的状态,是它因子的个数, 105 内最多的也就 128 个,我们可以记这个为 p .

      最后每个 f0[i] 乘上 i 所对的系数就行了.

      这样似乎可以直接倍增做(狄利克雷卷积的结合律?) 复杂度 Θ(m(plnp)logx)

      但我不会啊TAT...

      不知道这个性质就用矩阵快速幂就行了.

      构造一下这个转移 然后复杂度就是 Θ(mp3logx)

      然后加一下一些鬼畜的优化,就能跑到 6s 然而这题只给了 4s ..

      但我们可以继续优化qwq

      有一个叫做乘法同构的东西,如果两个数同构那么它们的答案(系数)是一样的.(总共 165 个)

      然后再预处理 2i 的矩阵,然后就可以做了qwq 复杂度 Θ(1653logx+m 1652logx)

      还有特别多鬼畜的做法(狄利克雷卷积,组合数学,DAG计数.....)

    • T3 : 瞎搞交互题

      给你一颗二叉树,一开始把你丢到深度为 initialDeep 的一个位置.. 每个点连出三条边,三种颜色表示.

      然后你可以进行两个操作 movequery . 分别是移动一个位置,查询你当前的深度.

      move 次数上限为 106 ; query 上限为 51000 . 让你在这些限制内走到根节点. ( initialDeep105 )

      不难发现 query 次数很少... 所以我们就可以多 move

      move 个七八遍就询问你当前的深度(不走回头路) 然后我们就可以求出你当前位置 x0 和之前位置 x1lca.

      我们就退回到那个位置就行了.

      为什么对呢qwq 你可以考虑一下期望... ( move 8 次 期望向上跳 1.9 层)

      下面是 solution 的讲法...

      我们发现有2分之一可能深度减1;4分之一可能深度减2;8分之一可能深度减3...

      于是愉快的在测距期望initialDeep/2次的情况下找到出口

  • 总结 :

    今天题目并不难, 可惜没勇气刚题...

    T1模型简单,应该多想一会的qwq

    T2那个矩阵快速幂想出来了,自以为能过 😦

    T3也不是很难,要多想点瞎搞啊!!!

    ak了三十人的壮观场景qwq Orz zhou888 ShichengXiao 日常ak!!!


  • T3-25 (出题人: __debug )

  • 得分情况 : 60+10+60=130

  • 题目:

    • T1 : 奇妙的dp(dp套dp)

      给出一个长度为 m 的序列 A, 请你求出有多少种 1...n 的排列, 满足 A 是它的一个 LIS.

      ( 1mn15 )

      原题是 BZOJ3591

      这道题的思想极其的巧妙,就是运用了dpdp 的思路.

      我们考虑我们 Θ(nlogn)LIS 的方法. 我们会用一个 fi 记忆 dp 值为 i 时候的最小的 val .

      然后这个可以每次二分查找,然后去更新.

      我们就可以把这当做 dp 的状态来转移.

      因为 fi 是满足单调递增的, 所以我们只要存在哪些数被我们选择了.

      然后我们就可以用三进制来模拟一下这个状态.

      0 表示没有考虑这个数; 1 表示考虑了这个数,并且这个数还在 f 数组中; 2 表示考虑了这个数,但这个数已经不存在 f 数组中了.

      然后压一下状态,然后枚举数字转移找位置的时候可以用一个单调队列去维护.

      那么总复杂度就是 Θ(n3n), 而且状态数很少跑的非常快.

      建议看看代码:

      #include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * fh; } void File() { freopen ("arg.in", "r", stdin); freopen ("arg.out", "w", stdout); } const int N = 16, NN = 14348908; int dp[NN], sta[N]; int id[N], n, a[N], nn, val[N], f[N], m; int ans = 0; int main () { File(); n = read(); m = read(); For (i, 1, m) { a[i] = read() - 1; id[a[i]] = i; if (i > 1 && a[i] < a[i - 1]) return puts("0"), 0; } sta[0] = 1; For (i, 1, n) sta[i] = sta[i - 1] * 3; dp[0] = 1; For (i, 0, sta[n] - 1) { if (!dp[i]) continue ; int x = i, cnt = 0, len = 0; For (j, 0, n - 1) { val[j] = x % 3; x = x / 3; if (val[j]) ++ cnt; if (val[j] == 1) f[len ++] = j; } if (cnt == n) { ans += dp[i]; continue ; } int pos = 0; For (j, 0, n - 1) { if (val[j]) continue ; if (id[j] > 1 && !val[a[id[j] - 1]]) continue ; while (pos < len && f[pos] < j) ++ pos; if (pos == m) continue ; int nxt = i + sta[j]; if (pos < len) nxt += sta[f[pos]]; dp[nxt] += dp[i]; } } printf ("%d\n", ans); return 0; }
    • T2 : 平面图,分治

      给定一个正 n 边形及其三角剖分, 共 2n3 条边 ( n 条多边形的边和 n3 条对角线), 每条边
      的长度为 1 .
      q 次询问, 每次询问给定两个点, 求它们的最短距离.

      ( 1n52000,1q2n )

      这道题思路我大概明白了,不想去写TAT

      挂一下题解算了.....

      由于给出的图是一个多边形的三角剖分, 因此每条对角线都将这个多边形分成了两部分, 考虑
      分治.

      每次在当前多边形中选出一条对角线使得两部分点数尽可能均匀,, 可以证明最坏情况下存在任
      意一侧不少于 n3 的分法.

      这条对角线两侧就形成了两个子多边形, 递归下去直到当前多边形为三角形为止, 并通过 bfs 计算出每个子多边形上每个点到选定的对角线两点的距离.

      询问的时候只要判断一下两个点是否在选定的对角线同侧, 如果是则递归求解, 否则可以通过
      之前预处理出的距离简单计算.
      找对角线的时候可以直接找环上距离最远的对角线.

      其实这题和 【ZJOI2016】旅行者 差不多

      都是平面图上求点对距离,代码比较繁琐.

      就是一开始利用了分治预处理的思路.

      这因为平面图的转移只能从一些特定的地方走,而不会去跳.

      那么我们每次预处理出里面一些特定点到其他所有点的距离就行了.

      有时间回来补补...

    • T3 : 最小割

      ​ 有一个 n×m 的地图, 地图上的每一个位置可以是空地, 炮塔或是敌人. 你需要操纵炮塔消灭
      敌人.
      ​ 对于每个炮塔都有一个它可以瞄准的方向, 你需要在它的瞄准方向上确定一个它的攻击位置,
      当然也可以不进行攻击. 一旦一个位置被攻击, 则在这个位置上的所有敌人都会被消灭.
      ​ 保证对于任意一个炮塔, 它所有可能的攻击位置上不存在另外一个炮塔.
      ​ 定义炮弹的运行轨迹为炮弹的起点和终点覆盖的区域. 你需要求出一种方案, 使得没有两条炮
      弹轨迹相交. 求能消灭的最多敌人.

      ( 1n,m50 )

      这题做法十分巧妙啊!!qwq

      小范围,加限制,而且很难思考出来正解...那么我们考虑一下网络流(费用流)吧2333

      这个建模十分的奇妙,先讲一下怎么建,再讲为什么这样建吧.

      1. 每个点拆成两个点,分别去考虑列和行的边. 我们记为 x0,x1. 然后 x0x1 有一条流量为 inf 的边.
      2. 超级源向每个纵向炮台( Sx0 ) 连一条流量为 inf 的边.
      3. 每个横向炮台向超级汇( x1T ) 连一条流量为 inf 的边.
      4. 然后考虑每个纵向炮台,它延伸到它能取的最大的值( maxv ) 的点,我们沿途连边 ( x0y0 ) ,流量为 maxvw ( w 是起点的权值,如果为负数那么我们设为0).
      5. 然后考虑每个横向炮台,也和上面相同,我们只是把边反过来了,并且这里的 w 是终点的权值.

      最后的答案就是 (maxv)cut. ( cut 为这个图的最小割)

      这是因为你考虑这个最小割的意义,就是你炮台撞车了,然后要沿路退回,损失的价值.(然后要使得这个损失最小)

      然后最后使得行列割断,那就是整个图就合法的情况,不存在两个炮台攻击区域相交.

      还有一点,为什么一定要拆点.

      不拆点的话,我们会发现,会有一种神奇的走法 列 列,那如果一个行和多个列相交,那就会十分的鬼畜.(答案会变大)

      此处拆点,限制了只能有一次从列变成行.

      _debugmatthew99 的方言是真的像qwq

    • 总结 :

      仍然没有过题,但暴力分拿的很足,比较开心qwq 而且很多都是原题,证明自己刷题不够啊!!

      今天的题解法都很神,而且很巧妙 没有太难的算法或者数据结构,但就是想不出...

      希望思维能提高啊!!! 日常orz zhou888 (A了T1, CF rank 3上首页) !!!


    • 3-26(出题人: __debug 讲题人 : h10 23333)

    • 得分情况 : 40+20+20=80 (成功诠释打铁滚粗)

    • T1:hash or kmp

      给定字符串 ST , 定义两个字符串匹配当且仅当 , 对于 T 的字符集有一个映射 , 然后使得 S 的一个子串和一一对应. (例如 3 4 3=2 1 23 4 32 2 2 )

      然后问你 S 上哪些位置和 T 对应.

      ( |S|,|T|106 字符集大小 C106 )

      看到这道题很容易想到 hash 可是我的hash状态选错了...

      虽然也求了一个 LastNext 数组可是它的位置不同我判不出来... (还调了4hour)

      我们可以这样思考,只要对于所有种类字符,存在一种它们排列方式唯一确定,且它的相对位置也唯一确定.

      那么我们可以这样做 记一个 Last[i] 为上一个和 i 这个位置上的字符一样的位置. Next[i] 同义,只是下一个.

      然后用 iLast[i] 之间的间隙 gap 来当做它的哈希值(平常我们用它的字符编号,作为标识,本题显然不适合咯)

      然后如果对于当前的 i 它的 Last[i] 不存在于当前你枚举的 S 的子串中,就跳过.

      而且在滑动的时候,对于你删去的位置也要剔去它的下一个位置的贡献.

      虽然好讲,代码却一点都不好写.... 调了很久qwq

      kmp 就是要魔改一波 fail 数组,就是它失配函数...(但我kmp不记得了啊啊)

      这题我也挂一下代码吧...(细节很重要啊) 怕挂掉打了双哈希,其实单的够了...

      #include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * fh; } void File() { freopen ("xiz.in", "r", stdin); freopen ("xiz.out", "w", stdout); } const int N = 1001000; int cases, Size; int n, m, S[N], T[N]; typedef unsigned long long ull; ull Pow1[N], Pow2[N]; const ull base1 = 7, base2 = 19260817; ull Hash1, Hash2, Board1, Board2; int Last[N], Next[N], Appear[N]; void Init(int maxn) { Pow1[0] = Pow2[0] = 1; For (i, 1, maxn) { Pow1[i] = Pow1[i - 1] * base1; Pow2[i] = Pow2[i - 1] * base2; } } vector<int> ans; int main () { File(); cases = read(); Size = read(); Init (1000000); while (cases --) { ans.clear(); n = read(); m = read(); For (i, 1, n) S[i] = read(); For (i, 1, m) T[i] = read(); Board1 = Board2 = 0; Set(Appear, 0); Set(Last, 0); For (i, 1, m) { Last[i] = Appear[T[i]]; if (!Last[i]) Last[i] = i; Next[Last[i]] = i; Appear[T[i]] = i; (Board1 += i - Last[i]) *= base1; (Board2 += i - Last[i]) *= base2; } Set(Appear, 0); Set(Last, 0); Set(Next, 0); For (i, 1, n) { Last[i] = Appear[S[i]]; if (!Last[i]) Last[i] = i; Next[Last[i]] = i; Appear[S[i]] = i; } Hash1 = Hash2 = 0; For (i, 1, n) { if (Last[i] > i - m) Hash1 += i - Last[i]; Hash1 *= base1; if (Last[i] > i - m) Hash2 += i - Last[i]; Hash2 *= base2; if (i < m) continue ; if (Hash1 == Board1 && Hash2 == Board2) ans.push_back(i - m + 1); int pos = i - m + 1; if (!Next[pos] || Next[pos] > i) continue ; Hash1 -= (Next[pos] - pos) * Pow1[i - Next[pos] + 1]; Hash2 -= (Next[pos] - pos) * Pow2[i - Next[pos] + 1]; } printf ("%d\n", (int)ans.size()); For (i, 0, ans.size() - 1) printf ("%d ", ans[i]); printf ("\n"); } return 0; }

    • T2 : 拉格朗日乘数

      在平面上找 n 个点, 要求这 n 个点离原点的距离分别为 r1,r2,...,rn . 最大化这 n 个点构成的
      凸包面积, 凸包上的点的顺序任意.(不要求点全在凸包上)

      (n8,ri1000)

      这题爬山和贪心轻松就有 90 分的高分...

      正解我原来也不知道是啥东西去搜了一波定义.

      设给定二元函数 z=f(x,y) 和附加条件 φ(x,y)=0 , 为寻找 z=f(x,y) 在附加条件下的极值点 , 先做拉格朗日函数 F(x,y,λ)=f(x,y)+λφ(x,y) , 其中 λ 是参数.

      F(x,y,λ)xyλ 的一阶偏导数等于零,即

      {Fx=fx(x,y)+λφx(x,y)=0Fy=fy(x,y)+λφy(x,y)=0Fλ=φ(x,y)=0

      由上述方程组解出 x,yλ , 如此求得的 (x,y) , 就是函数 z=ƒ(x,y) 在附加条件 φ(x,y)=0 下的可能极值点.

      若这样的点只有一个 , 由实际问题可直接确定此即所求的点.

      大概定义是这样的吧 知乎上有更好的介绍 qwq 在知乎上学算法2333

      这个可以运用在本题上,只是这题变成高维,换为多元函数.

      此处的 z=12(r1r2sin(θ1)+r2r3sin(θ2)+...+rnr1sin(θn)), φ(θ1,...,θn)=0

      也就是当 i=1nθi=2π 时要使得面积和 S 最大.

      此处乘数 λ=r1r2cos(θ1)=r2r3cos(θ2)=...=rnr1cos(θn). 至于为啥,导入之前的偏导就可以了qwq

      导数运算高中会学的... 这里只需要简单的套公式就行了..

      此处 θ1,θ2,...,θn 关于 λ 单调.

      然后你就二分一下 λ 然后去 check 一下(用之前的约束条件 φ 就行了)

      最后算答案.... 但 check 那里的 arccos 的两个出口看不太懂...(就是 cos<1>1 会有两种不同的判断)

      以后有时间再回来补补这些东西吧....

      ps : 最近求导和积分好多啊 , 怕不是微积分专题...

    • T3 : 线段树合并和分裂

      给定一个长度为 n 的正整数序列 a1...an . 现在有 m 次操作, 分为两种:

      • 1 l r t : 将区间 [l,r] 降序排序 (t=0) 或升序排序 (t=1)
      • 2 l r : 询问区间 [l,r] 内元素之积的十进制下最高位

      (n,m2105,ain)

      这题我看到就想到原来做过的一道题 BZOJ4552 ...

      当初只会咸鱼的二分答案转化成01序列 然后线段树维护....

      后来看了下 fjzzq大佬的博客 发现可以直接用线段树分裂和合并去维护.

      这种东西往往都能玄学地达到正确复杂度...

      利用这种动态开点的值域线段树可以解决一堆有序集合进行合并/分裂/查询k小的问题,最好用的就是在排序问题中。

      然后这题有个巧妙的技巧... 就是每个数取个 log10 然后乘法变成加法 , 只要维护加法就行了. 然后答案就是小数点后一位的数字... 精度根本不会萎qwq

  • 总结 :

    今天决策失误了... T1明明知道自己的那个方法很难打而且容易挂 , 还花那么久去打 , 真是石乐志

    后面两题部分分很多啊!!! 希望以后能多冷静一会,整体把控.. 一道题要估计细节多 要超过 2h 那就把优先级放低.

    然后找最容易拿的分去做qwq


__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/8631696.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(943)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示