AtCoder Beginner Contest 265

前三题太简单就不写了。

D - Iroha and Haiku (New ABC Edition)

考虑将用前缀和代替区间加法,暴力枚举 \(x\) 的位置,二分一下 \(y,z,w\) 即可,时间复杂度为 \(O(n\log n)\)

E - Warp

写爆搜肯定会爆,而记忆化又不如 dp 好理解,考虑 dp,设 \(f[dep][x][y]\) 代表当前走了 \(dep\) 步到了点 \((x,y)\),注意到每个点可以多次经过,所以还要记一下当前是第几步这个点。

然后暴力枚举每个操作使用了多少次,假设操作 AB 使用了 \(i\) 次,操作 CD 使用了 \(j\) 次,操作 EF 使用了 \(k\) 次,那么现在点的坐标就是 \((x=i*A+j*C+k*E,y=i*B+j*D+k*F)\),如果这个点没有障碍,那么就有 \(f[dep][x][y]=f[dep-1][x-A][y-B]+f[dep-1][x-C][y-D]+f[dep-1][x-E][y-F]\) 注意下操作次数非负。

如果 \(f\) 用 map 来存,时间复杂度为 \(O(n^3\log n^3)\) 难以通过,用 hash 来存容易冲突并且空间也开不下太大,用 hash 表可能可以通过,但下面介绍一种更简单的方法。

注意我们关心的是到什么位置和到这个位置走了多少步,所以我们可以记下每个操作用了多少次,那么依旧可以记录下上面的 dp 所记录的信息,具体的设 \(f[i][j][k]\) 表示操作 AB 使用了 \(i\) 次,操作 CD 使用了 \(j\) 次,操作 EF 使用了 \(k\) 次,那么就是走了 \(i+j+k\) 步,位置同上,转移有 \(f[i][j][k]=f[i-1][j][k]+f[i][j-1][k]+f[i][j][k-1]\),这样 \(f\) 用普通的数组即可,至于记一下哪些点有障碍直接用 set 即可,时间复杂度为 \(O(n^3\log m)\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
const int N=307;
const int mod=998244353;
int f[N][N][N];
set<pair<int,int> >s;
main() 
{
   int n,m,A,B,C,D,E,F,ans=0;
	cin>>n>>m>>A>>B>>C>>D>>E>>F;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%lld%lld",&x,&y);
		s.insert(mp(x,y));
	} 
	f[0][0][0]=1;
	for(int i=0;i<=n;i++)
	    for(int j=0;i+j<=n;j++)
	        for(int k=0;i+j+k<=n;k++)
	        {
	        	 int x=i*A+j*C+k*E;
					int y=i*B+j*D+k*F;
					if(s.find(mp(x,y))!=s.end()||i+j+k==0)continue;
					if(i!=0)f[i][j][k]+=f[i-1][j][k];
					if(j!=0)f[i][j][k]+=f[i][j-1][k];
					if(k!=0)f[i][j][k]+=f[i][j][k-1];
					f[i][j][k]%=mod;
				}
	for(int i=0;i<=n;i++)
	    for(int j=0;i+j<=n;j++)
			ans+=f[i][j][n-i-j],ans%=mod;
	printf("%lld",ans);
   return 0;
}
posted @ 2022-09-13 14:43  xiaoPanda  阅读(90)  评论(0)    收藏  举报