CF Round 418(div2) E题解

官方题解
官方题解的不完全翻译?
这题一开始想了个 O(n6) 的DP, f[i][j][k][a][b] 表示从j到k到首都距离为i,且上一段有a个1和b个2剩下。但时空应该都过不去,本来妄图靠多项式自带小常数卡一卡,写到一半发现越写越虚,都开始怀疑这个DP的正确性了,然后去抄题解。
最后我写的是 O(n5) 的DP,手打1+6+12行转移,竟然只WA了一发,对我这种蒟蒻来说真是不可思议啊。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<list>
#include<fstream>
#include<cmath>
#include<cctype>
#include<stack>
#include<cstdlib>
#include<ctime>
#include<ext/pb_ds/priority_queue.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef tree<int,null_type,std::less<int>,rb_tree_tag,tree_order_statistics_node_update> SI;
typedef unsigned int ui;
typedef double db;
inline int getint(){
    int x=0;
    char c=getchar();
    while(!isdigit(c))c=getchar();
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return x;
}
const int N=55,M=3000;
const ll mo=1000000007;
int d[N],i,n;
map<ll,ll> f;
inline ll pow(ll x,int y){
    if(!y)return 1;
    ll u=pow(x,y>>1);
    if(y&1)return u*u%mo*x%mo;
        else return u*u%mo;
}
inline ll C2(int x){
    return x*(x-1)>>1;
}
ll dp(int v,int a,int b,int cc,int dd){
    int w=v*n*n*n*n+a*n*n*n+b*n*n+cc*n+dd;
    if(a<0 || b<0 || cc<0 || dd<0)return 0;
    if(v>n)return !a && !b && !cc && !dd; 
    if(f.count(w))return f[w];
    ll&ans=f[w];
    if(a==0 && b==0){
        ans=(dp(v+1,cc-1,dd,d[v]==2,d[v]==3)*cc+dp(v+1,cc+1,dd-1,d[v]==2,d[v]==3)*dd)%mo;
    }else{
        if(d[v]==2){
            ans=(ans+dp(v+1,a-1,b,cc+1,dd)*a)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc+1,dd)*b)%mo;
            ans=(ans+dp(v+1,a-1,b,cc-1,dd)*a*cc)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc-1,dd)*b*cc)%mo;
            ans=(ans+dp(v+1,a-1,b,cc+1,dd-1)*a*dd)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc+1,dd-1)*b*dd)%mo;
        }else{
            ans=(ans+dp(v+1,a-1,b,cc,dd+1)*a)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc,dd+1)*b)%mo;
            ans=(ans+dp(v+1,a-1,b,cc,dd)*a*cc)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc,dd)*b*cc)%mo;
            ans=(ans+dp(v+1,a-1,b,cc+2,dd-1)*a*dd)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc+2,dd-1)*b*dd)%mo;
            ans=(ans+dp(v+1,a-1,b,cc-2,dd)*C2(cc)*a)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc-2,dd)*C2(cc)*b)%mo;
            ans=(ans+dp(v+1,a-1,b,cc,dd-1)*cc*dd*a)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc,dd-1)*cc*dd*b)%mo;
            ans=(ans+dp(v+1,a-1,b,cc+2,dd-2)*C2(dd)*a)%mo;
            ans=(ans+dp(v+1,a+1,b-1,cc+2,dd-2)*C2(dd)*b)%mo;
        }
    }
    return ans;
}
int main(){
    cin>>n;
    for(i=1;i<=n;++i)scanf("%d",d+i);
    cout<<dp(3,d[1]==2,d[1]==3,d[2]==2,d[2]==3)<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值