[UNR #6]机器人表演

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[UNR #6]机器人表演相关的知识,希望对你有一定的参考价值。

机器人表演

题解

相当离谱的状态定义。
看到这道题我们首先比较容易想到的方法是 d p dp dp
如果我们要 d p dp dp的话必然可能会涉及同一个串通过不同路径同时贡献到答案的问题,也就是说,我们要尽可能让每个串属于一个唯一的状态。
考虑该怎么定义状态。
当前串如果是由不同的方式转移过来的,那么它就只与它在原串中转移到的长度有关,因为确定它在原串中使用的前缀后,自然而然就可以知道它新增了几个 0 / 1 0/1 0/1了。
所以一种方法是在状态中记录能够转移到这个点的原串前缀集合,这样来状压 d p dp dp

但显然这是不太可能过我们 n ⩽ 300 n\\leqslant 300 n300的数据范围的,也就是说,我们不太可能把整个集合都记录下来。
那我们尝试考虑一下,只记录集合中的最大值,能否通过回撤还原整个集合。
显然,如果当前最大值下一个就是我们要扩展的元素,那么就可以直接扩展。否则,我们考虑能不能通过加入 0 / 1 0/1 0/1的形式扩展,再不行就考虑回撤。
如果我们下一个是 0 0 0不行需要撤回的话,现在肯定是处在临时加入的 0 0 0已经填满的状况,不可能再填出更多的 0 0 0了,所以回撤也是没有解的。
如果下一个位置要填 1 1 1,但我们现在填不动,那我们肯定是临时加入的 0 0 0的数量与 1 1 1一样。所以现在我们只需要回撤找到第一个 0 0 0数量大于 1 1 1的点即可。
实际上,这个回撤也是相当于在进行子序列的 n e x t _ p e r m u t a t i o n next\\_permutation next_permutation的枚举,只取了当前集合最小点的一个代表元,就能这样枚举还原整个集合。
当然,我们不太可能每次转移的时候都去找到它会回退到哪个点,这个可以预处理出来。
其它的转移就是按当前的最优填数方案填即可。

时间复杂度 O ( n m 2 ) O\\left(nm^2\\right) O(nm2)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,LL> pii;
#define MAXN (1<<20)+5
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=998244353;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=15;
const int INF=0x3f3f3f3f;
const double Pi=acos(-1.0);
const double eps=1e-9;
const int lim=1000000;
const int orG=3,ivG=332748118;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
    while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
    x*=f;

LL gcd(LL a,LL b)return !b?a:gcd(b,a%b);
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,dp[305][305][305],pre[305];
char str[305];
int main()
    read(n);read(m);scanf("%s",str+1);
    for(int i=1;i<=n;i++)
        int now=(str[i+1]=='1');pre[i]=i;
        while(pre[i]>=0&&now<=0)
            now+=(str[pre[i]]=='0'?1:-1),pre[i]--;
    
    dp[0][0][0]=1;
    for(int i=0;i<n+m+m;i++)
        for(int x=0;x<=min(i,m);x++)
            for(int y=0;x+y<=i&&y<=x;y++)
                int z=i-x-y;if(z>n||!dp[x][y][z])continue;
                if(x<m&&str[z+1]!='0')Add(dp[x+1][y][z],dp[x][y][z],mo);
                if(y<x&&str[z+1]!='1')Add(dp[x][y+1][z],dp[x][y][z],mo);
                if(z<n)Add(dp[x][y][z+1],dp[x][y][z],mo);
                if(str[z+1]!='1'&&x==y&&pre[z]>=0)
                    int t=(z-pre[z]+1)/2;if(x+t>m)continue;
                    Add(dp[x+t][y+t][pre[z]],dp[x][y][z],mo);
                
            
    printf("%d\\n",dp[m][m][n]);
    return 0;


谢谢!!!

以上是关于[UNR #6]机器人表演的主要内容,如果未能解决你的问题,请参考以下文章

[UNR #6]机器人表演

[UNR #6]稳健型选手

[UNR #6]稳健型选手

[UNR #6]稳健型选手

UOJ#308UNR#2UOJ拯救计划

[UOJ388]UNR #3配对树