【P5196】【USACO19JAN】Cow Poetry G

前言

这是我们今天课上一道练习,结果全班就我一个过了。

看到这道题我就有了思路(

不过还是调了很久。

Solution

题意明白,不多赘述。

首先考虑对于一行诗,凑足 \(k\) 个音节有几种方案。

这个很类似于零一背包问题,和采药几乎一样。

我们设 \(f[i]\) 表示凑成 \(k\) 个音节的方案总数。

枚举最后一个单词,得到转移方程:

\[f[i]=\sum f[i-s[q]] \]

其中 \(s[q]\) 就是某个单词的长度,只有 \(i\ge s[q]\) 时才能转移。


一行诗的情况解决,然后考虑一整首诗。

由题意,两行诗押韵只考虑最后一个字母的韵部。

那么容易想到在上面的方程上加一维,表示最后一个单词的韵部,将之改为:

\[f[i][j]=\sum f[i-s[q]][p] \]

\(p\) 为任意的韵部。

聪明的你会发现,这一次转移是 \(n^2\) 的。

但是我们发现,\(p\) 的枚举是不必要的。

\(sum[i]=\sum f[i][p]\),则上面的转移方程变为 \(f[i][j]=\sum sum[i-s[q]]\)

时间复杂度骤降为 \(O(n)\)

然后是如何计算总方案数?很简单。

我们设 \(X_i\) 表示 字母 \(i\) 出现的次数。

则我们可以令某一个字母 \(i\) 押的韵是 \(j\),因为题目还没有要求不同字母押不同的韵,所以这些答案可以使用乘法原理和加法原理加起来。。

推一下柿子得到结果

\[\prod_{i=1}^{26}\sum_{p=1}^n f[k][p]^{X[i]} \]

其中 \(\Pi\) 枚举字母,\(k\) 是题目给的参数,\(p\) 是任意颜色。


代码

#include <stdio.h>
typedef long long ll;
namespace root
{
#define lc(id) (id<<1)
#define rc(id) (id<<1|1)
#define lowbit(id) (id&-id)
#define repeat(times) while(times--)
	static const int Buf_size=1<<25,Int_size=25;
	static char F[Int_size];
	static char _c;
	static bool _f;
	static int _x,__cnt;
	static const signed int base_10=10,zero(48),nine(57),flag_signed(45);
	inline int abs(const int &_x)
	{
		return _x<0?-_x:_x;
	}
	inline int max(const int &_x,const int &_y)
	{
		return _x>_y?_x:_y;
	}
	inline int min(const int &_x,const int &_y)
	{
		return _x<_y?_x:_y;
	}
	inline void swap(int &_x,int &_y)
	{
		static int _z;
		_z=_x;
		_x=_y;
		_y=_z;
		return;
	}
#define digit() (zero<=_c&&_c<=nine)
	char buf[Buf_size],*p1=buf,*p2=buf;
//#define getchar() *p1++
	static char obuf[Buf_size],*p3=obuf;
#define putchar(x) (p3-obuf<Buf_size)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
//	template<typename _Tp,const bool is_signed=false>inline void read(_Tp&_x){if(!is_signed){_x=0;_c=getchar();while(!digit())_c=getchar();while(digit()){_x=_x*10+(_c^zero);_c=getchar();}return;}else{_x=0;_f=false;_c=getchar();while(!digit()){if(_c==flag_signed)_f=true;_c=getchar();}while(digit()){_x=_x*10+(_c^zero);_c=getchar();}if(_f)_x=-_x;return;}}
	inline int read()
	{
		_x=0;
		_c=getchar();
		while(!digit())_c=getchar();
		while(digit())
		{
			_x=_x*10+(_c^zero);
			_c=getchar();
		}
		return _x;
	}
	template<typename _Tp>inline void write(_Tp _x)
	{
		if(_x<0)
		{
			putchar(flag_signed);
			_x=-_x;
		}
		if(_x<base_10)
		{
			putchar(_x^zero);
			return;
		}
		write(_x/base_10);
		putchar(_x%base_10^zero);
	}
	inline void out(int x,const char end='\n')
	{
		if(!x)
		{
			putchar(zero);
			putchar(end);
			return;
		}
		if(x<0)
		{
			x=-x;
			putchar(flag_signed);
		}
		__cnt=0;
		while(x)
		{
			F[++__cnt]=x%base_10;
			x/=base_10;
		}
		while(__cnt)
		{
			putchar(F[__cnt]^zero);
			--__cnt;
		}
		putchar(end);
		return;
	}
}//一些函数,缺省源。
namespace solve
{
	const int mod=1000000007;//记得取模
	using namespace root;
	long long base,ret;
	int repow(int a,int b)
	{
		int res=1;
		for(;b;b>>=1)
		{
			if(b&1)
				res=(ll)res*a%mod;
			a=(ll)a*a%mod;
		}
		return res;
	}//快速幂
	char now;
	long long res=1,mid;
	int sum[5005],s[5005],c[5005],t[5005],f[5005][5005];
	void main()
	{
//		fread(buf,1,Buf_size,stdin);
		register int i,j;
//		freopen("poetry.in","r",stdin);
//		freopen("poetry.out","w",stdout);
		sum[0]=1;
		register int n=read(),m=read(),k=read();
		for(i=1; i<=n; ++i)
		{
			s[i]=read();
			c[i]=read();
		}
		for(i=1; i<=k; ++i)
		{
			for(j=1; j<=n; ++j)
			{
				if(i>=s[j])
				{
					(f[i][c[j]]+=sum[i-s[j]])%=mod;
					(sum[i]+=sum[i-s[j]])%=mod;
				}
			}//这就是转移一行内的方案
		}
		for(i=1; i<=m; ++i)
		{
			++t[getchar()-'A'];//t[]就是上面的X[]
			getchar();
		}
		for(i=0; i<26; ++i)
		{
			if(t[i]==0)
				continue;
			mid=0;
			for(j=1; j<=n; ++j)
				mid+=repow(f[k][j],t[i]);
			(res*=mid%mod)%=mod;
		}//这就是最后那个柿子
		printf("%lld",res);
		return;
	}
}
int main()
{
	solve::main();
	return 0;
}
posted @ 2023-02-05 17:41  Syara  阅读(22)  评论(0编辑  收藏  举报