【学习笔记】[ARC089F] ColoringBalls

毒瘤题

发现操作序列的长度只和 B B B的数目有关,并且操作序列一定形如 r b ? ? ? . . . rb???... rb???...的形式,这个不太好证

考虑暴力 D P DP DP的本质

B B B的数目为 k i k_i ki,发现应该按 k i k_i ki从大到小加入,过程中 维护 b b b的匹配情况,这样转移是 O ( 1 ) O(1) O(1)

发现这样处理不了最后一段的情况,交上去 w a wa wa了一半

发现可以枚举 k i = 0 k_i=0 ki=0的数目 x 1 x_1 x1以及 k i ≥ 1 k_i\ge 1 ki1的数目 x 2 x_2 x2,然后把前 x 1 + x 2 x_1+x_2 x1+x2 a a a删掉,转移第 i i i段的时候就对第 i i i b b b和第 i + 1 i+1 i+1 b b b中间的部分去匹配 ? ? ?就好了。有点抽象

这个做法常数非常小。所以肯定是可以通过的。

这个做法复杂度 O ( n 6 log ⁡ n ) O(n^6\log n) O(n6logn)

考虑这个做法的本质

发现只用枚举 x 1 + x 2 x_1+x_2 x1+x2就好了,这样优化到了 O ( n 5 log ⁡ n ) O(n^5\log n) O(n5logn)

应该有更加高妙的贪心,但是我太菜了完全想不出来。

#include
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
const int mod=1e9+7;
int n,K;
string str;
ll fpow(ll x,ll y=mod-2){
    ll z(1);
    for(;y;y>>=1){
        if(y&1)z=z*x%mod;
        x=x*x%mod;
    }return z;
}
ll fac[75],inv[75],invnum[75];
void init(int n){
    fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
    inv[n]=fpow(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
    for(int i=1;i<=n;i++)invnum[i]=inv[i]*fac[i-1]%mod;
}
void add(ll &x,ll y){
    x=(x+y)%mod;
}
int sum[75],sum2[75],rk[75],rk2[75],sum3[75],cnt;
unordered_map<int,ll>dp[75][75][75];
ll binom(int x,int y){
    if(x<0||y<0||x<y)return 0;
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int calc(int x,int y){
    return x*75+y;
}
ll dfs(int i,int j,int k,int l,int x){
    if(j>n+1)return 0;int tmp=calc(l,x);
    if(dp[i][j][k].find(tmp)!=dp[i][j][k].end())return dp[i][j][k][tmp];
    if(k==cnt){
        if(l>=0)return binom(n+1,j);
        return 0;
    }
    if(i==0)return 0;
    ll res=0;
    //不选
    add(res,dfs(i-1,j,k,l,0));
    //选
    int tot=sum3[rk2[k+2]]-sum3[rk2[k+1]];
    add(res,dfs(i,j+2*i-1,k+1,min(0,l-i+tot),x+1)*invnum[x+1]);
    add(res,dfs(i,j+2*i,k+1,min(0,l-i+tot),x+1)*invnum[x+1]*2);
    add(res,dfs(i,j+2*i+1,k+1,min(0,l-i+tot),x+1)*invnum[x+1]);
    return dp[i][j][k][tmp]=res;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>K>>str,init(n+1);
    for(int i=1;i<=K;i++){
        sum[i]=sum[i-1],sum2[i]=sum2[i-1];
        if(str[i-1]=='r')sum[i]++,rk[sum[i]]=i;
        else sum2[i]++;
    }ll res=0;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=sum2[K]&&i+j<=min(n,sum[K]);j++){
            int x=1;
            for(int k=1;k<=K;k++){
                if(str[k-1]=='b'&&x<=j&&k>rk[x])rk2[x++]=k;
                sum3[k]=sum3[k-1];if(str[k-1]=='b'||sum[k]>i+j)sum3[k]++;
            }if(x<=j)continue;rk2[x]=K+1;sum3[K+1]=sum3[K]+1;
            for(int k=1;k<=n;k++){
                for(int l=0;l<=n+1;l++){
                    for(int m=0;m<=j;m++){
                        dp[k][l][m].clear();
                    }
                }
            }
            cnt=j,add(res,dfs(n,cnt+2*i,0,0,0)*fac[i+j]%mod*inv[i]%mod);
        }
    }cout<<res;
}

你可能感兴趣的:(学习,笔记)