codeforces1178F1/F2

32 篇文章 0 订阅
12 篇文章 0 订阅

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][l1]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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值