题解 糖果
这题我有一部分水了还有不明白的地方……建议再问问
首先这题显然是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;
}