jzoj6377. 【NOIP2019模拟2019.10.05】幽曲[埋骨于弘川]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

真的都快忘了。
首先,我们考虑排序,求出一个神奇的排列方式,也就是dfn序。
那么答案必定是在dfn序里面一些连续的段连接起来。
然后我们就判断这玩意儿是否满足在a里面出现过。

于是现在分两步走:

第一步,假如现在有一个数字A,我们要判断其是否可以出现在a里面。
我们现在就有一种构造思路就是:从高位开始构造,假设现在构造到第i位,一直加个位之后,满足当前这位等于数字A的第i位后,继续构造第i-1位。
那么我们发现可以用一个神奇的DP来实现这个判断过程。

f i , p , x f_{i,p,x} fi,p,x表示当前做到第i位,最高位到第i位数字已经填完后最大的数字为p,个位为x开始,让第i位+1后个位变成 f i , p , x f_{i,p,x} fi,p,x
这个就可以从低位开始转移到高位,每次让低位进位k次即可。注意这个p,不然会被搞崩心态。
然后再设一个 g i , j , p , x g_{i,j,p,x} gi,j,p,x表示的东东和上面类似,只是这个j表示的是让第i位进位到j。
转移的话就从j-1位进过去,利用f数值即可。

第二步,开始统计答案。
我们设 d p i , j , p , x dp_{i,j,p,x} dpi,j,p,x表示当前走到dfn序中的第i位,当前弄出来的数字是第j位,且当前位置放的是 d [ d f n [ i ] ] d[dfn[i]] d[dfn[i]],最大值为p,各位从x开始的方案数。
我们发现,这个i是可以从 f a i fa_i fai i i i这一段区间内转移过来的。
所以说,我们可以利用前缀和优化一下,时间复杂度就变成 O ( n 2 ∗ k 2 ) O(n^2*k^2) O(n2k2)

小细节巨多无比。

标程

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
const long long mo=998244353;
const int maxn=501;
const int maxm=11;

int n,m,a[maxn],x[maxn],y[maxn],gs;
int f[maxn][maxm][maxm];
int g[maxn][maxm][maxm][maxm];
long long dp[maxn][maxn][maxm][maxm],sum[maxn][maxn][maxm][maxm];
int tot,nex[maxn*2],las[maxn*2],tov[maxn*2];
int dfn[maxn],wz[maxn],fa[maxn],dep;
int tot1,nex1[maxn*2],las1[maxn*2],tov1[maxn*2];
int son[maxn][maxn];

__attribute__((optimize("-O3")))
void qsort(int x,int l,int r)
{
	int i=l;int j=r;
	int m=son[x][(i+j)/2];
	while (i<=j)
	{
		while (son[x][i]>m) i++;
		while (son[x][j]<m) j--;
		if (i<=j)
		{
			swap(son[x][i],son[x][j]);
			i++;j--;
		}
	}
	if (l<j) qsort(x,l,j);
	if (r>i) qsort(x,i,r); 
}

__attribute__((optimize("-O3")))
void insert(int x,int y)
{
	tot++;
	tov[tot]=y;
	nex[tot]=las[x];
	las[x]=tot;
}

__attribute__((optimize("-O3")))
void insert1(int x,int y)
{
	tot1++;
	tov1[tot1]=y;
	nex1[tot1]=las1[x];
	las1[x]=tot1;
}

__attribute__((optimize("-O3")))
void dfs(int x,int ff)
{
	gs++;
	dfn[x]=gs;
	wz[gs]=x;
	for (int i=las1[x];i>0;i=nex1[i])
	{
		if (tov1[i]!=ff)
		{
			fa[tov1[i]]=x;
			dfs(tov1[i],x);
		}
	}
}

__attribute__((optimize("-O3")))
void dfssort(int x,int ff)
{
	for (int i=las[x];i>0;i=nex[i])
	{
		if (tov[i]!=ff)
		{
			son[x][0]++;
			son[x][son[x][0]]=tov[i];
			dfssort(tov[i],x);
		}
	}
	qsort(x,1,son[x][0]);
	for (int i=1;i<=son[x][0];i++)
	{
		insert1(x,son[x][i]);
		insert1(son[x][i],x);
	}
}

__attribute__((optimize("-O3")))
int main()
{
//	freopen("data.in","r",stdin);
	freopen("buried.in","r",stdin);
	freopen("buried.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (register int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for (register int i=1;i<n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		insert(x[i],y[i]);
		insert(y[i],x[i]);
	}
	dfssort(1,0);
	dfs(1,0);
	for (register int i=1;i<=n;i++)
	{
		for (register int p=0;p<m;p++)
		{
			for (register int x=0;x<m;x++)
			{
				f[i][p][x]=-1;
				for (register int a=0;a<m;a++)
				{
					g[i][p][a][x]=-1;
				}
			}
		}
	}
	for (register int i=0;i<m;i++)
	{
		for (register int x=0;x<m;x++)
		{
			if (i>0 || (i==0 && x>0))
			{
				register int j=x;
				while (j<m)
				{
					g[1][i][j][x]=j;
					j=j+max(j,i);
				}
				f[1][i][x]=j%m;
				g[2][i][1][x]=j%m;
			}
		}
	}
	for (register int i=0;i<m;i++)
	{
		for (register int x=0;x<m;x++)
		{
			g[2][i][0][x]=x;
			if (i>0 || (i==0 && x>0))
			{
				register int j=f[1][i][x];
				for (register int cs=2;cs<m;cs++)
				{
					g[2][i][cs][x]=f[1][max(i,cs-1)][j];
					j=f[1][max(i,cs-1)][j];
				}
			}
		}
	}
	
	for (register int i=2;i<=n;i++)
	{
		for (register int p=0;p<m;p++)
		{
			for (register int x=0;x<m;x++)
			{
				if (p>0 || (p==0 && x>0))
				{
					register int op=x;
					g[i][p][0][x]=x;
					for (register int j=0;j<m;j++)
					{
						op=f[i-1][max(p,j)][op];
						if (j<m-1) g[i][p][j+1][x]=op;
					}
					f[i][p][x]=op;
				}
			}
		}
	}
	for (register int i=3;i<=n;i++)
	{
		for (register int p=0;p<m;p++)
		{
			for (register int x=0;x<m;x++)
			{
				if (p>0 || (p==0 && x>0))
				{
					register int op=f[i-2][m-1][g[i-1][p][m-1][x]];
					g[i][p][1][x]=op;
				}
			}
		}
	}
	for (register int i=3;i<=n;i++)
	{
		for (register int p=0;p<m;p++)
		{
			for (register int x=0;x<m;x++)
			{
				g[i][p][0][x]=x;
				if (p>0 || (p==0 && x>0))
				{
					register int j=g[i][p][1][x];
					for (register int cs=2;cs<m;cs++)
					{
						g[i][p][cs][x]=f[i-1][max(p,cs-1)][j];
						j=f[i-1][max(p,cs-1)][j];
					}
				}
			}
		}
	}
	
	for (register int i=1;i<=n;i++)
	{
		dp[1][i][a[1]][g[i][0][a[1]][1]]=1;
		sum[1][i][a[1]][g[i][0][a[1]][1]]=1;
	}
	for (register int i=2;i<=n;i++)
	{
		for (register int p=0;p<m;p++)
		{
			for (register int x=0;x<m;x++)
			{
				for (register int w=1;w<=n-i+1;w++)
				{
					if (g[w][p][a[wz[i]]][x]>=0)
					{
						dp[i][w][max(p,a[wz[i]])][g[w][p][a[wz[i]]][x]]=
						(dp[i][w][max(p,a[wz[i]])][g[w][p][a[wz[i]]][x]]+sum[i-1][w+1][p][x]-sum[dfn[fa[wz[i]]]-1][w+1][p][x]+mo)%mo;
					}
				}
			}
		} 
		memcpy(sum[i],sum[i-1],sizeof(sum[i-1]));
		for (register int w=1;w<=n-i+1;w++)
		{
			for (register int x=0;x<m;x++)
			{
				for (register int p=0;p<m;p++)
				{
				//	if (g[w][p][a[wz[i]]][x]>=0)
					{
						sum[i][w][p][x]=
						(sum[i][w][p][x]+dp[i][w][p][x])%mo;
					}
				}
			}
		} 
	}
	long long ans=0;
	for (register int i=1;i<=n;i++)
	{
		for (register int j=a[wz[i]];j<m;j++)
		{
			ans=(ans+dp[i][1][j][a[wz[i]]])%mo;
		}
	}
	printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值