题解 糖果

传送门

这题我有一部分水了还有不明白的地方……建议再问问

首先这题显然是DP,问题在于如何做DP
发现 \(c\) 的序列未知,完全不可转移,那尝试只用 \(a\)\(b\) 建立状态
这样的话我们需要一些形如「当 \(a\)\(b\) 已经×××,则 \(c\) 一定×××」的结论
那首先 \(a\)\(b\) 选的数肯定不同
\(a\)\(b\) 选了一个数时, \(c\) 在之前一定没有选过这个数
当a和b都没有选择序列中的某个值时,c一定在之前选过这个数了
那么题解说可以根据这些性质建立DP
\(dp[i][j][k]\) 表示 \(a\) 序列选到 \(i\)\(b\) 序列选到 \(j\)\(c\) 序列中有k个占位符,这些位置具体选什么数还没有确定时的方案数
想想如何转移
可以分别枚举 \(a,b\) 序列可选位置跳到的下一个位置 \(i',j'\)
那么分几种情况:
\(a\) 选中了在 \(i\) 位置的数,它对方案数造成的影响是把转移流推向考虑 \(j\) 转移的部分了
\(b\) 选中了在 \(j\) 位置的数,它对方案数造成的影响是把转移流再推回下一次考虑 \(i\) 的转移的部分
\(a\)\(b\) 不选当前这个位置上的数,那它一定在之前被 \(c\) 选过了,也就确定了一个 \(c\) 中留空的位置
然后统计答案
为什么要乘 \((3*i-1)*(3*i-2)\) 没听太懂,留坑

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 510
#define ll long long 
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int a[N], b[N];
const ll p=1e9+7;

namespace force{
	int c[N], ans;
	bool vis[N];
	void solve() {
		int lim=n/3;
		for (int i=1; i<=n; ++i) c[i]=i;
		do {
			memset(vis, 0, sizeof(bool)*(n+5));
			int pos1=1, pos2=1, pos3=1;
			for (int i=1; i<=lim; ++i) {
				while (vis[a[pos1]]) ++pos1;
				while (vis[b[pos2]]) ++pos2;
				while (vis[c[pos3]]) ++pos3;
				if (a[pos1]==b[pos2] || a[pos1]==c[pos3] || b[pos2]==c[pos3]) goto jump;
				vis[a[pos1++]]=1, vis[b[pos2++]]=1, vis[c[pos3++]]=1;
			}
			++ans;
			jump: ;
		} while (next_permutation(c+1, c+n+1));
		printf("%lld\n", ans%p);
		exit(0);
	}
}

namespace task{
	ll dp1[N][N][N], dp2[N][N][N], ans, posa[N], posb[N];
	void solve() {
		int m=n/3;
		for (int i=1; i<=n; ++i) posa[a[i]]=i, posb[b[i]]=i;
		dp1[1][1][0]=1;
		for (int i=1; i<=n+1; ++i) 
			for (int j=1; j<=n+1; ++j) 
				for (int k=0; k<=m; ++k) {
					//cout<<n<<' '<<i<<' '<<j<<' '<<k<<endl;
					if (dp1[i][j][k]) {
						if (i!=n+1) {
							if (posb[a[i]]<j) dp1[i+1][j][k]=(dp1[i+1][j][k]+dp1[i][j][k])%p; //, cout<<"pos1"<<endl;
							else {
								//cout<<"pos2 "<<dp1[i][j][k]<<endl;
								dp2[i][j][k]=(dp2[i][j][k]+dp1[i][j][k])%p;
								if (k) dp1[i+1][j][k-1]=(dp1[i+1][j][k-1]+dp1[i][j][k]*k%p)%p;
							}
						}
						else if (!k) ans=(ans+dp1[i][j][k])%p;
					}
					if (dp2[i][j][k]) {
						if (posa[b[j]]<i) dp2[i][j+1][k]=(dp2[i][j+1][k]+dp2[i][j][k])%p;
						else if (b[j]!=a[i]) {
							dp1[i+1][j+1][k+1]=(dp1[i+1][j+1][k+1]+dp2[i][j][k])%p;
							if (k) dp2[i][j+1][k-1]=(dp2[i][j+1][k-1]+dp2[i][j][k]*k%p)%p;
						}
					}
				}
		for (int i=1; i<=m; ++i) {
			ans = (ans*(3*i-1)%p*(3*i-2))%p;
		}
		printf("%lld\n", ans%p);
		exit(0);
	}
}

signed main()
{
	n=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	for (int i=1; i<=n; ++i) b[i]=read();
	//force::solve();
	task::solve();
	
	return 0;
}
posted @ 2021-08-15 06:36  Administrator-09  阅读(26)  评论(0编辑  收藏  举报