F1
题目描述
题目大意
有n次染色,每次选择一个纯色区间[ai,bi],将其染成颜色i
求能染成最终状态的染色方案数
题目保证每种颜色至少出现一次且不存在未被染色的格子
题解
F1是F2的弱化版,而且F1并不难想
虽然比赛时没想出来
设min[l][r]表示[l,r]中最小的颜色
设id[i]表示颜色i出现的位置(只可能有一种)
因为m=n,所以每种颜色刚好出现一次
设f[i][j]表示覆盖完[i,j]所需要的方案数
那么显然最后一次覆盖的颜色为min[i][j]
考虑该颜色的覆盖范围,可以将其拆成四个区间来计算(因为不能跨过纯色的边界,再加上id[min[i][j]])
则
f
[
i
]
[
j
]
=
∑
l
=
i
i
d
[
m
i
n
[
i
]
[
j
]
]
∑
r
=
i
d
[
m
i
n
[
i
]
[
j
]
]
j
f
[
i
]
[
l
−
1
]
∗
f
[
l
]
[
i
d
[
m
i
n
[
i
]
[
j
]
]
−
1
]
∗
f
[
i
d
[
m
i
n
[
i
]
[
j
]
]
+
1
]
[
r
]
∗
f
[
r
+
1
]
[
j
]
f[i][j]=\sum_{l=i}^{id[min[i][j]]}{\sum_{r=id[min[i][j]]}^{j}{f[i][l-1]*f[l][id[min[i][j]]-1]*f[id[min[i][j]]+1][r]*f[r+1][j]}}
f[i][j]=∑l=iid[min[i][j]]∑r=id[min[i][j]]jf[i][l−1]∗f[l][id[min[i][j]]−1]∗f[id[min[i][j]]+1][r]∗f[r+1][j]
这样做是O(m4)的,考虑分别枚举l和r,把计算的到的各自的积乘起来,时间复杂度O(m3)
code
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define mod 998244353
using namespace std;
int a[501];
long long f[502][501];
int id[501];
int mn[501][501];
int n,m,i,j,k,l,s,len;
long long s1,s2;
int main()
{
// freopen("f1.in","r",stdin);
scanf("%d%d",&n,&m);
fo(i,1,m)
{
scanf("%d",&a[i]);
id[a[i]]=i;
}
fo(i,1,m)
{
mn[i][i]=a[i];
fo(j,i+1,m)
mn[i][j]=min(mn[i][j-1],a[j]);
}
f[1][0]=1;
fo(i,1,m)
{
f[i][i]=1;
f[i+1][i]=1;
}
fo(len,2,m)
{
fo(i,1,m-len+1)
{
j=i+len-1;
s=id[mn[i][j]];
s1=0;
s2=0;
fo(k,i,s)
s1=(s1+f[i][k-1]*f[k][s-1]%mod)%mod;
fo(l,s,j)
s2=(s2+f[s+1][l]*f[l+1][j]%mod)%mod;
f[i][j]=(f[i][j]+s1*s2%mod)%mod;
}
}
printf("%I64d\n",f[1][m]);
}
F2
显然合法的情况的颜色块(连续最大相同颜色段)不超过2n个
(如果超过2n个那么就肯定不合法了)
而且覆盖时也不可能把颜色块分开
所以压缩一下就可以把m变成n级别的了
先判是否合法(每个必然存在的区间内不能有更小的颜色)
同样设f[i][j],考虑min[i][j]的最早和最晚出现位置(可能有多个位置)
转移类似,但要考虑[最早位置,最晚位置]这一段内拆开后可能有其他的颜色段,同样乘起来
要注意拆开后不能把原先必然存在的颜色段分开(有≥2个已知颜色的)
code
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define mod 998244353
using namespace std;
int a[1001];
long long f[1002][1001];
int mn[1001][1001];
int bg[1001][1001];
int ed[1001][1001];
int Bg[501];
int Ed[501];
int sum[1001];
int Sum[1001];
int n,m,i,j,k,l,s,S,len,ls;
long long s1,s2,s3;
int main()
{
// freopen("f2.in","r",stdin);
scanf("%d%d",&n,&m);
ls=0;
l=0;
fo(i,1,m)
{
scanf("%d",&j);
if (j!=ls)
{
if (l>=1000)
{
printf("0\n");
return 0;
}
a[++l]=j;
ls=j;
}
}
m=l;
fo(i,1,m)
{
if (!Bg[a[i]])
Bg[a[i]]=i;
Ed[a[i]]=i;
mn[i][i]=a[i];
bg[i][i]=i;
ed[i][i]=i;
fo(j,i+1,m)
if (mn[i][j-1]<a[j])
{
mn[i][j]=mn[i][j-1];
bg[i][j]=bg[i][j-1];
ed[i][j]=ed[i][j-1];
}
else
if (mn[i][j-1]>a[j])
{
mn[i][j]=a[j];
bg[i][j]=j;
ed[i][j]=j;
}
else
{
mn[i][j]=mn[i][j-1];
bg[i][j]=bg[i][j-1];
ed[i][j]=j;
}
}
fo(i,1,n)
{
fo(j,Bg[i],Ed[i])
if (i>a[j])
{
printf("0\n");
return 0;
}
}
f[1][0]=1;
fo(i,1,m)
{
f[i][i]=1;
f[i+1][i]=1;
}
fo(len,2,m)
{
fo(i,1,m-len+1)
{
memset(sum,0,sizeof(sum));
memset(Sum,0,sizeof(Sum));
memset(Bg,0,sizeof(Bg));
memset(Ed,0,sizeof(Ed));
j=i+len-1;
s=bg[i][j];
S=ed[i][j];
fo(k,i,j)
{
if (!Bg[a[k]])
Bg[a[k]]=k;
Ed[a[k]]=k;
}
fo(k,1,n)
if (Bg[k]<Ed[k])
{
++sum[Bg[k]+1];
--sum[Ed[k]+1];
++Sum[Bg[k]];
--Sum[Ed[k]];
}
fo(k,i,j)
{
sum[k]+=sum[k-1];
Sum[k]+=Sum[k-1];
}
s1=0;
s2=0;
s3=1;
fo(k,i,s)
if (!sum[k])
s1=(s1+f[i][k-1]*f[k][s-1]%mod)%mod;
fo(l,S,j)
if (!Sum[l])
s2=(s2+f[S+1][l]*f[l+1][j]%mod)%mod;
ls=s;
fo(k,s+1,S)
if (a[k]==mn[i][j])
{
s3=s3*f[ls+1][k-1]%mod;
ls=k;
}
f[i][j]=(f[i][j]+s1*s2%mod*s3%mod)%mod;
}
}
printf("%I64d\n",f[1][m]);
}