洛谷13310 染紫

题目

雪有一棵大小为 n 的树。

雪定义一种树上的染色方案的权值:

a 为其红色极大连通块的大小的平方的和。

b 为其蓝色极大连通块的大小的平方的和。

这种染色方案的权值为 ab

树上一些点已经被染上了红或蓝色,请将剩余点分别染成红或蓝色,求所有合法染色方案的权值和。

设待染色节点的个数为 C,则所有合法染色方案共有 2C 个。

答案可能很大,请对 998244353 取模。

做法

连通块size平方求和,很典的套路

假设只有一种颜色求所有连通块Σsize^2,做法是把问题转为 求在连通块里选两个不同点的方案数,这样一个连通块的总计算次数显然是size^2,和原问题结果一样

本题有两种颜色,求的是(Σ红i的size)*(Σ蓝j的size),拆开之后等于对任意两个红蓝连通块算乘积;一个连通块内部等价于选两个点,那么原问题等价于 对于树上所有染色方案,分别在一个红连通块和一个蓝连通块里各选两个点 的方案数

连通块内部计算总次数为size12*size22,所有连通块加起来算得(Σ红i的size)*(Σ蓝j的size),最后计算时考虑了 选择的块外颜色不同 时 算作不同方案

\(f[t][0/1][0/1][0/1][0/1]\),表示子树t内,t颜色选择0/1(color),t同色连通块中1,2号点是否选(d1,d2,当d1=d2时定义变为 已在子树内同色块中选出两个点),以及子树内是否有已在异色块内选出两个点(all);一个同色块在结算(父亲是异色点)时要求d1=d2,当一个块选出两个点时就会永久保留,合并到异色父亲时变为all_father=1,再异色就变回d1=d2=1


设父亲状态为\(f[t][color][d1][d2][all]\),儿子为\(f[son][color\_son][d1\_son][d2\_son][all\_son]\),转移为\(f[t][...]*f[son][...] \to f_{new}[t][...]\)

树上dp,分同色异色转移,同色块合并(选重了就非法),异色块要求d1_son=d2_son,考虑父亲儿子中两种颜色的合并(t同色:all_son和d1,d2;t异色:d1_son,d2_son和all)

\(ans=f[1][0][1][1][1]+f[1][1][1][1][1]\)

(设成all主要是因为空间不够不能设r1r2b1b2,同时这样设不用考虑具体颜色,只考虑父亲儿子是否相同

code

dfs爆栈90,于是写成bfs形式

//#pragma comment(linker, "/STACK:10240000000,10240000000")
#include <bits/stdc++.h>
#define fo(a,b,c) for (int a=b; a<=c; a++)
#define fd(a,b,c) for (int a=b; a>=c; a--)
#define add(a,b) a=((a)+(b))%mod
#define mod 998244353
#define ll long long
#define file
using namespace std;

const int N=2e6+10;
int n,T;
int a[N*2][2],ls[N],len;
ll f[N][2][2][2][2];
ll F[2][2][2][2];
int bz[N];
char st[N];

void New(int x,int y)
{
	++len;
	a[len][0]=y;
	a[len][1]=ls[x];
	ls[x]=len;
}

void init(int t)
{
	if (bz[t]!=1)
	{
		fo(r1,0,1)
		fo(r2,0,1)
		f[t][0][r1][r2][0]=1;
	}
	if (bz[t]!=0)
	{
		fo(b1,0,1)
		fo(b2,0,1)
		f[t][1][b1][b2][0]=1;
	}
}

void up(int t,int son)
{
	memset(F,0,sizeof(F));
	fo(c,0,1) fo(d1,0,1) fo(d2,0,1) fo(all,0,1)
	fo(C,0,1) fo(D1,0,1) fo(D2,0,1) fo(All,0,1)
	{
		if (c!=C)
		{
			if (D1==D2)
			{
				if (all==1 && D1==1) continue;
				int newall;
				if (all==0 && D1==1 || all==1 && D1==0) newall=1;
				else newall=0;
				
				if (All==0)
				add(F[c][d1][d2][newall],f[t][c][d1][d2][all]*f[son][C][D1][D2][All]);
				else
				if (d1==0 && d2==0)
				add(F[c][1][1][newall],f[t][c][d1][d2][all]*f[son][C][D1][D2][All]);
			}
		}
		else
		{
			if (d1==1 && D1==1) continue;
			if (d2==1 && D2==1) continue;
			if (all==1 && All==1) continue;
			
			add(F[c][d1|D1][d2|D2][all|All],f[t][c][d1][d2][all]*f[son][C][D1][D2][All]);
		}
	}
	
	memcpy(f[t],F,sizeof(F));
}

//void dfs(int Fa,int t)
//{
//	init(t);
//	for (int i=ls[t]; i; i=a[i][1])
//	if (a[i][0]!=Fa)
//	{
//		dfs(t,a[i][0]);
//		up(t,a[i][0]);
//	}
//}

int d[N],dH,dT;
int faBfs[N];
void bfs()
{
	dH=0,dT=1;
	d[1]=1;
	memset(faBfs,255,sizeof(faBfs));
	faBfs[1]=0;
	while (dH+1<=dT)
	{
		int t=d[++dH];
		for (int i=ls[t]; i; i=a[i][1])
		if (faBfs[a[i][0]]==-1)
		{
			d[++dT]=a[i][0];
			faBfs[a[i][0]]=t;
		}
	}
	
	fd(id,n,1)
	{
		int t=d[id];
		init(t);
		for (int i=ls[t]; i; i=a[i][1])
		if (a[i][0]!=faBfs[t])
		up(t,a[i][0]);
	}
}

void solve()
{
    scanf("%d",&n);
    fo(i,1,n-1)
    {
    	int x,y;
    	scanf("%d%d",&x,&y);
    	New(x,y),New(y,x);
	}
	scanf("%s",st+1);
	fo(i,1,n)
	{
		switch (st[i])
		{
			case 'w':{bz[i]=-1;break;}
			case 'r':{bz[i]=0;break;}
			case 'b':{bz[i]=1;break;}
		}
	}
	
	bfs();
	
	ll ans=0;
	add(ans,f[1][0][1][1][1]);
	add(ans,f[1][1][1][1][1]);
	add(ans,mod);
	printf("%lld\n",ans);
}

int main()
{
//	freopen("T597882.in","r",stdin);

    T=1;
    //scanf("%d",&T);
    for (;T;--T) solve();

    return 0;
}
posted @ 2025-07-17 13:04  gmh77  阅读(17)  评论(0)    收藏  举报