1 条题解

  • 0
    @ 2026-8-3 22:34:00

    dpx,i,jdp_{x,i,j} 表示 xx 子树内有 ii 种颜色需要与祖先匹配,有 jj 种独立的颜色不需要匹配,此时的方案数。

    可以发现转移到的点就是 ([maxi,i],[maxj,j])([\max i,\sum i],[\max j,\sum j]) 这个矩形,只需要分别记录 $(\max i,\max j),(\max i,\sum j),(\sum i,\max j),(\sum i,\sum j)$ 四个状态后二维前缀和即可。

    最后还需要加入点 xx 的贡献,也就是分成两种情况,xx 在集合内的时候可以转移到 (i,j)(i1)(i,j)(i\ge1)(i+1,j)(i+1,j)xx 不在集合内的时候可以转移到 (i1,j+1)(i1)(i-1,j+1)(i\ge1)(i,j+1)(i,j+1)

    为了避免算重,只需要把上面的四个状态中的 i\sum i 改为 i+1\sum i+1,最后再转移到 (i,j)(i,j)(i1,j+1)(i-1,j+1) 即可(需要保证 i1i\ge1)。

    但是这样会算漏,因为即使 xx 不在集合内,也可以与祖先有相同的颜色,因此也可以转移到 (i,j)(i,j)

    但是这样又算重了,为什么呢?因为实际上如果两种转移 xx 都不在集合内,一种情况下与祖先没有相同的颜色,一种情况下与祖先有相同的颜色,最终得到相同的 cc 序列,这样就会算两遍。

    考虑怎么处理这个问题,我们可以使用类似延迟钦定的技巧,因为一个子树内的任何一个点不论是否在集合内,都可以与祖先有相同的颜色。我们将不在集合内的点与祖先的匹配延迟考虑,或者说在固定 i+ji+j 的情况下只保留 ii 最小的状态,等到需要的时候再匹配,这样就不会算重了(也可以理解为对 i+ji+j 相同的状态按照 ii 做差分)。

    现在转移就不一样了,我们转移到的点变成了一个矩形的左边界和上边界,这样只需要记三个顶点同样可以前缀和维护。

    还有一个小问题,就是我们刚才说的延迟匹配,也就是如果要合并 (ix,jx)(ix,jx)(iy,jy)(iy,jy)(假设 ixiyix\le iy),它们转移到的颜色数最小的状态(也就是矩形的左下顶点)并不是直接合并得到的 (iy,max{jx,jy})(iy,\max\{jx,jy\}),而是 (iy,jx(iyix))(iy,jx-(iy-ix))(iy,jy)(iy,jy) 合并得到的 (iy,max{jx(iyix),jy})(iy,\max\{jx-(iy-ix),jy\})

    这样我们就以 O(n4)O(n^4) 的时间复杂度完成了这道题,代码很短,只有 2.2k。

    #include <bits/stdc++.h>
    #include "rainbow.h"
    using namespace std;
    const int mod = 998244353;
    inline void inc(int &x, int y) { x = (x + y >= mod ? x + y - mod : x + y); }
    inline void dec(int &x, int y) { x = (x < y ? x - y + mod : x - y); }
    inline int add(int x, int y) { return (x + y >= mod ? x + y - mod : x + y); }
    vector<int> v[210];
    int siz[210], dp[210][210][210], f[210][210][210][3], g[210][210][3], s1[210][210], s2[210][210];
    void dfs(int x)
    {
    	siz[x] = 1;
    	f[x][1][0][0] = 1;
    	f[x][1][0][1] = 1;
    	f[x][1][0][2] = 1;
    	for (auto y : v[x])
    	{
    		dfs(y);
    		for (int ix = 0; ix <= siz[x]; ix++) for (int jx = 0; ix + jx <= siz[x]; jx++) for (int iy = 0; iy <= siz[y]; iy++) for (int jy = 0; iy + jy <= siz[y]; jy++)
    		{
    			if (ix <= iy) inc(g[iy][max(jx - (iy - ix), jy)][0], 1ll * f[x][ix][jx][0] * dp[y][iy][jy] % mod);
    			else inc(g[ix][max(jx, jy - (ix - iy))][0], 1ll * f[x][ix][jx][0] * dp[y][iy][jy] % mod);
    			inc(g[max(ix, iy)][jx + jy][1], 1ll * f[x][ix][jx][1] * dp[y][iy][jy] % mod);
    			inc(g[ix + iy][jx + jy][2], 1ll * f[x][ix][jx][2] * dp[y][iy][jy] % mod);
    		}
    		siz[x] += siz[y];
    		for (int i = 0; i <= siz[x]; i++) for (int j = 0; i + j <= siz[x]; j++)
    		{
    			f[x][i][j][0] = g[i][j][0], g[i][j][0] = 0;
    			f[x][i][j][1] = g[i][j][1], g[i][j][1] = 0;
    			f[x][i][j][2] = g[i][j][2], g[i][j][2] = 0;
    		}
    	}
    	for (int i = 0; i <= siz[x]; i++) for (int j = 0; i + j <= siz[x]; j++) s1[i][j] = s2[i][j] = 0;
    	for (int i = 0; i <= siz[x]; i++) for (int j = 0; i + j <= siz[x]; j++)
    	{
    		inc(s1[i][j], f[x][i][j][0]);
    		dec(s1[i][j], f[x][i][j][1]);
    		inc(s2[i][j], f[x][i][j][1]);
    		dec(s2[i + 1][j], f[x][i][j][2]);
    	}
    	for (int i = 0; i <= siz[x]; i++) for (int j = 1; i + j <= siz[x]; j++) inc(s1[i][j], s1[i][j - 1]);
    	for (int i = 1; i <= siz[x]; i++) for (int j = 0; i + j <= siz[x]; j++) inc(s2[i][j], s2[i - 1][j]);
    	for (int i = 0; i <= siz[x]; i++) for (int j = 0; i + j <= siz[x]; j++)
    	{
    		int val = add(s1[i][j], s2[i][j]);
    		inc(dp[x][i][j], val);
    		if (i >= 1) inc(dp[x][i - 1][j + 1], val);
    	}
    }
    int rainbow(int c, int n, vector<int> fa)
    {
    	for (int i = 1; i < n; i++) v[fa[i] + 1].push_back(i + 1);
    	dfs(1);
    	int ans = 0;
    	for (int i = 0; i <= n; i++) inc(ans, dp[1][0][i]);
    	return ans;
    }
    

    后记

    赛时我在这道题上花费了两个小时,但是最终发现会算重放弃了这道题,获得了 00 分,这也导致我 Day 2 爆炸获得了 85+20+0=10585+20+0=105 的分数。

    这两天我一直在思考如果我不去做 T3 而是拼 T2 暴力或者冲 T2 正解会怎么样?似乎肯定会得到更高的分数,甚至可能接近或达到 Au 线。

    回过头来再看这道题,我才意识到我的场上做法距离正解其实只有一步,可当时我的心态已经接近崩溃,没有办法进行有效的思考了。

    即便如此,我并不后悔当时跳过 T2 做 T3 的这个决定。某种意义上,我是幸运的——我遵从了那一刻的直觉,赌过一次,并且已经无限接近胜利,只是最后差了那么一点运气。

    也许,这正是 OI 最残忍也最迷人的地方:你永远不知道那一步之外是什么,但你也永远会为迈出那一步而心动。

    • 1

    信息

    ID
    12612
    时间
    1000ms
    内存
    1100MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者