Gravatar
梦那边的美好ET
积分:6999
提交:1286 / 2721


题目4076  小b爱旅行      3      评论
2025-10-24 11:19:14    
Gravatar
梦那边的美好ET
积分:6999
提交:1286 / 2721


题目4077  小b爱取模      3      评论
2025-10-24 11:18:43    
Gravatar
梦那边的美好ET
积分:6999
提交:1286 / 2721

Subtask1: n,m≤100

注意到我们可以枚举每一对 (E,S) 并判断 Bob 是否必胜,最后将必胜的方案数除以 nm 就是答案。

判断是否必胜我们直接将边 E 删去并从起点 S 开始搜索即可,时间复杂度 O(n2m)。期望得分 30pts。

Subtask2: w=1

除非 S 是叶子且 E 恰好是 S 的唯一出边,Bob 都是必胜的。统计是简单的,不再赘述。

Subtask3: n,m≤5000

考虑从边双连通分量入手,我们枚举 E。

若 E 不是割边,那么删除 E 不会影响 S 所能到达的点/边集。只要原图中还存在边权与 E 相等的边,那 Bob 就是必胜的,贡献为 n。

若 E 是割边,那么删除 E 会将边双树分为两个连通块,而 S 只能遍历其所处的连通块,所以 Bob 必胜等价于该连通块中有边权与 E 相等的边。直接遍历两个连通块来判断即可。

割边的数量可以达到与 n 同阶,故时间复杂度为 O(nm)。期望得分 60pts。

Subtask3: n,m≤106

考虑优化 E 是割边时的判断过程。注意到一个连通块必定是边双树中的一颗子树或者全树扣掉一颗子树,而后者可以直接用全局减去该子树得到。于是问题等价于求一棵子树中边权与根的父边相等的边的数量。

这个问题的做法非常多,容易 O(n) 实现,期望得分 100pts。


题目3920  编辑题目      3      1 条 评论
2025-10-24 11:15:02    
Gravatar
llbc1234
积分:20
提交:7 / 20

首先来看这个什么三元组。


定义一种特殊的三元组:(x,y,z),其中x,y,z都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:



x,y,z 是整数,x<y<z,y−x=z−y;x,z 颜色相同。



满足上述条件的三元组的分数规定为 (x+z)×(number_x+number_z)。


诶,我们发现,这个「分数」跟 y 之间,半个咕值的关系都没有啊 QAQ?

于是,秒懂:

∵y−x=z−y

∴x+z=2y

又,2y 是偶数,所以 x,z 同奇偶。

这就是 y 的用处啦QAQ。


由于不同颜色的 x,z 肯定不会产生分数,所以我们可以先把这个「狭长的纸带」按照颜色分类,最后把每种颜色产生的分数加起来即可。

然后不同奇偶性的 x,z 也不会产生分数,所以把每个颜色种类按照奇偶性再分个类,最后把奇数产生的分数和偶数产生的分数加起来即可。

举个例子:

格子编号   1 2 3 4 5 6 7 8 9 10

格子上的数字 5 5 3 2 2 2 7 8 2 5

格子颜色   2 2 1 1 2 1 2 2 2 1

那么先按照颜色分类:

颜色为 1 的:

格子编号   3 4 6 10

格子上的数字 3 2 2 5

格子颜色   1 1 1 1

颜色为 2 的:

格子编号   1 2 5 7 8 9

格子上的数字 5 5 2 7 8 2

格子颜色   2 2 2 2 2 2

再按照编号分类:

颜色为 1 ,编号为奇数的:

格子编号   3

格子上的数字 3

格子颜色   1

颜色为 1 ,编号为偶数的:

格子编号   4 6 10

格子上的数字 2 2 5

格子颜色   1 1 1

颜色为 2 ,编号为奇数的:

格子编号   1 5 7 9

格子上的数字 5 2 7 2

格子颜色   2 2 2 2

颜色为 2 ,编号为偶数的:

格子编号   2 8

格子上的数字 5 8

格子颜色   2 2

好的,分类完毕!

那么,怎么计算分数呢?



当然可以 O(n2) 暴力算一通。做法显然,这里不多说了。

不过,复杂度铁定爆炸。

考虑更优的做法。

拿上面那个例子中,颜色为 2 ,编号为奇数的 4 个格子来举个例子:

由于颜色显然是一样的,而且计算分数也和颜色无关,所以就不用再管颜色了。

然后设 f[i] 为这一组中第 i 个数的编号,n[i] 为这一组中第 i 的数的颜色。

i  1 2 3 4

f[i]1 5 7 9

n[i]5 2 7 2

先看前两个数,他们产生的分数为:

(f[1]+f[2])×(n[1]+n[2])

然后考虑当第三个数加入时,多出来的分数。

第三个数和第一个数会产生一些分数:

(f[1]+f[3])×(n[1]+n[3])

第三个数和第二个数也会产生一些分数:

(f[2]+f[3])×(n[2]+n[3])

所以多出来的分数为:

(f[1]+f[3])×(n[1]+n[3])+(f[2]+f[3])×(n[2]+n[3])

展开后,得到:

f[1]⋅n[1]+f[1]⋅n[3]+f[3]⋅n[1]+f[3]⋅n[3]+f[2]⋅n[2]+f[2]⋅n[3]+f[3]⋅n[2]+f[3]⋅n[3]

把 n[3] 和 f[3] 提取出来:

f[1]⋅n[1]+f[1]⋅n[3]+f[3]⋅n[1]+f[3]⋅n[3]+f[2]⋅n[2]+f[2]⋅n[3]+f[3]⋅n[2]+f[3]⋅n[3]

(标红的是提取出来的 n[3],标蓝的是提取出来的 f[3])

n[3]⋅(f[1]+f[2]+f[3])+f[3]⋅(n[1]+n[2]+n[3])+n[1]⋅f[1]+n[2]⋅f[2]

从这个式子中,我们看出,只需要处理 f 数组,n 数组,还有 f[i]⋅n[i] 的前缀和即可。

后面也是一个一个添加进来,一样的。


该题解来自洛谷




题目2112  [NOIP 2015PJ]求和      评论
2025-10-23 20:30:02    
Gravatar
李金泽
积分:320
提交:23 / 45

题目大意:给出一个 $n$ 点 $m$ 边的 DAG,每个点有两个权值 $a_i$,$b_i$.保证 $a, b$ 均为 $1\sim n$ 的排列。有三种操作:交换 $a_{x}$, $a_{y}$、交换 $b_{x}$, $b_{y}$、找数。

找数:给定x, l, r,找出最大的$b_{y}$满足x能到达y,l≤$a_{y}$≤r

数据范围:1≤n, q≤1e5,1≤m≤2e5,数据组数不超过3


解法思路:

本题核心为bitset的运用。

由DAG的连通性可知,最优时间复杂度不超过O($\frac{nm}{\omega}$),其中$\omega$=64,所以可以乱搞。

可以先用拓扑把能到达y的集合算出来,将可达性和范围限制取交集。

容易发现l≤$a_{y}$≤r可转换为$a_{y}$≥l和$a_{y}$≥r+1的异或值

现在考虑如何求$a_{y}$≥l

考虑分块,将$a_{y}$≥l分为$a_{y}$≥x·s和l≤$a_{y}$<x·s,其中x= ⌈$\frac{l}{s}$⌉ ,s=$\sqrt{n}$

设v[x]为$a_{y}$≥x·s的集合,可直接与l≤$a_{y}$<x·s部分取并。因为bitset比较单个元素时间复杂度为O(1),所以l≤$a_{y}$<x·s部分暴力计算即可

考虑修改v[x],设在交换$a_{x}$,$a_{y}$时$a_{x}$≤$a_{y}$,将所有满足$a_{x}$<x·s≤$a_{y}$的v[x]中删去$a_{x}$并加入$a_{y}$

将已经得到的合法y的集合设为B,求$b_{x}$的最大值。

容易想到把$b_{y}$也按值域分块,找到最大的l使$b_{y}$≥l的集合与B有交集

二分找时间复杂度接近O(nq),肯定不行。考虑找的过程中有什么性质。

容易发现l不断增大的过程中集合元素只减不增,因此前面比较结果不变(全0才会比较下一个ull块),所以可以保留前面的比较结果。找到答案所在块后再在块内暴力比较。

修改部分与v[x]一样。

这里的比较方式比较特殊,需要手写bitset。

时间复杂度O($\frac{n(m+q)}{\omega}+q\sqrt{n}$)。






题目4116  [统一省选 2025]追忆 AAAAAAAAAAAAAAAAAAAAAAAAA      2      评论
2025-10-22 22:09:18    
Gravatar
LikableP
积分:1408
提交:346 / 950

3946. 信使 题解

题目描述

给定一个 $n$ 个点 $m$ 条边的有向图,$q$ 次询问从点 $u$ 走 $d$ 步到达点 $v$,且只经过点 $u,v$ 各一次的路径数。答案对 $z$ 取模。

题解

原题目要求只经过起点和终点一次的路径数,这个不太好求。但是如果没有这个限制,只要求任意两点之间走 $d$ 步的路径数的话,还是很好求哒!

设 $f[i][j][d]$ 表示:从点 $i$ 走 $d$ 步无限制到达点 $j$ 的路径数。

for (int i = 1; i <= n; ++i)
   for (int j = 1; j <= n; ++j) if (e[i][j])
       f[i][j][1] = 1;
for (int d = 1; d <= 50; ++d)
   for (int i = 1; i <= n; ++i)
       for (int j = 1; j <= n; ++j)
           for (int k = 1; k <= n; ++k) if (e[k][j])
               f[i][j][d] = (f[i][j][d] + f[i][k][d - 1]);

那么要求从点 $i$ 走 $d$ 步不经过点 $i,j$ 到达点 $j$ 的路径数,就转化为了从点 $i$ 走 $d$ 步无限制到达点 $j$ 的路径数减去从点 $i$ 走 $d$ 步中途经过了点 $i$ 或点 $j$ 到达点 $j$ 的路径数

简单来说就是,正难则反。合法的方案不好求,就用总方案减去不合法的方案。

设 $h[i][j][d]$ 表示:从点 $i$ 走 $d$ 步不经过点 $i,j$ 到达点 $j$ 的路径数 。想要不合法就在中途的第 $k$ 步($1 \le k \lt d$)走到 $i$ 或 $j$ 即可。

  • 假如第 $k$ 步走到了点 $j$,那么不合法路径数就是:

    从点 $i$ 走 $k$ 步不经过点 $i,j$ 到达点 $j$ 的路径数乘上从点 $j$ 走 $(d - k)$ 步无限制到达点 $j$ 的路径数,即:

    $$h[i][j][k] * f[j][j][d - k]$$

  • 假如第 $k$ 步走到了点 $i$,那么不合法路径数应该是:

    从点 $i$ 走 $k$ 步不经过点 $i,j$ 到达点 $i$ 的路径数乘上从点 $i$ 走 $(d - k)$ 步无限制到达点 $j$ 的路径数

    发现缺少一个状态,于是定义 $g[i][j][k]$ 表示从点 $i$ 走 $k$ 步不经过点 $i,j$ 到达点 $i$ 的路径数。于是这种情况不合法路径数就是:

    $$g[i][j][k] * f[i][j][d - k]$$

    注意这里不能使用 $h[i][i][k]$,因为这样只保证了中途不经过点 $i$(思考 $h$ 和 $g$ 在定义上的不同)。

那么从点 $i$ 走 $d$ 步中途经过点 $i$ 或点 $j$ 到达点 $j$ 的路径数 $x$ 即为:

$$x = g[i][j][k] * f[i][j][d - k] + h[i][j][k] * f[j][j][d - k]\quad(1 \le k \lt d)$$

那么 $h[i][j][d] = f[i][j][d] - x$。

接下来考虑如何计算 $g[i][j][d]$(从点 $i$ 走 $d$ 步不经过点 $i,j$ 到达点 $i$ 的路径数)。

同样地,考虑正难则反,求出不合法的路径数,再用总路径数减去不合法的路径数即可。

类似,想要不合法,只需要在中途的第 $k$ 步($1 \le k \lt d$)走到点 $i$ 或点 $j$ 即可。

  • 假如第 $k$ 步走到了点 $i$,那么不合法路径数应该是:

    从点 $i$ 走 $k$ 步不经过点 $i,j$ 到达点 $i$ 的路径数乘上从点 $i$ 走 $(d - k)$ 步无限制到达点 $i$ 的路径数,即:

    $$g[i][j][k] * f[i][i][d - k]$$

    注意这里不能使用 $h[i][i][k]$,因为这样只保证了中途不经过点 $i$(思考 $h$ 和 $g$ 在定义上的不同)。

  • 假如第 $k$ 步走到了点 $j$,那么不合法路径数应该是:

    从点 $i$ 走 $k$ 步不经过点 $i,j$ 到达点 $j$ 的路径数乘上从点 $j$ 走 $(d - k)$ 步无限制到达点 $i$ 的路径数,即:

    $$h[i][j][k] * f[j][i][d - k]$$

那么从点 $i$ 走 $d$ 步途中经过点 $i$ 或点 $j$ 到达点 $j$ 的路径数 $y$ 为:

$$y = g[i][j][k] * f[i][i][d - k] + h[i][j][k] * f[j][i][d - k] \quad (1 \le k \lt d)$$

那么 $g[i][j][d] = f[i][j][d] - y$。

这些操作都是在询问之前进行的,每次询问的时候直接输出 $h[u][v][d]$(从点 $u$ 走 $d$ 步不经过点 $u,v$ 到达点 $v$ 的路径数)即可。

代码

#include <iostream>
using namespace std;
typedef long long ll;

const int MAXN = 110;
const int MAXD = 60;

ll f[MAXN][MAXN][MAXD]; // f[i][j][d]: tot solution of from i to j with no condition
ll h[MAXN][MAXN][MAXD]; // h[i][j][d]: tot solution of from i to j without passing i or j
ll g[MAXN][MAXN][MAXD]; // g[i][j][d]: tot solution of from i to i without passing i or j

int n, m, q;
int e[MAXN][MAXN];
ll z;

int main() {
  freopen("messenger.in", "r", stdin);
  freopen("messenger.out", "w", stdout);
  cin.tie(0)->sync_with_stdio(false), cout.tie(0);
  cin >> n >> m >> z;
  for (int i = 1, a, b; i <= m; ++i) {
    cin >> a >> b;
    e[a][b] = 1;
  }

  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      if (e[i][j]) f[i][j][1] = h[i][j][1] = 1;
    }
  }

  for (int d = 2; d <= 50; ++d) {
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= n; ++j) {
        for (int k = 1; k <= n; ++k) {
          if (e[k][j]) f[i][j][d] = (f[i][j][d] + f[i][k][d - 1]) % z;
        }
      }
    }
  }

  for (int d = 2; d <= 50; ++d) {
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= n; ++j) {
        if (i == j) continue;
        // store the illegal paths to h[][][] and g[][][] first
        for (int k = 1; k < d; ++k) {
          h[i][j][d] = (h[i][j][d] + g[i][j][k] * f[i][j][d - k] + h[i][j][k] * f[j][j][d - k]) % z;
          g[i][j][d] = (g[i][j][d] + g[i][j][k] * f[i][i][d - k] + h[i][j][k] * f[j][i][d - k]) % z;
        }
        h[i][j][d] = (f[i][j][d] - h[i][j][d] + z) % z;
        g[i][j][d] = (f[i][i][d] - g[i][j][d] + z) % z;
      }
    }
  }

  cin >> q;
  for (int u, v, d; q--; ) {
    cin >> u >> v >> d;
    cout << h[u][v][d] << endl;
  }
  return 0;
}

题目3946  信使 AAAAAAAAAA      4      3 条 评论
2025-10-05 14:38:13