BZOJ 4416 阶乘字符串

状压dp。状态有点难想,方程十分简单。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 455
using namespace std;
int t,n,g[maxn][23],f[(1<<22)+5],len,regis[23];
char s[maxn];
void work()
{
    memset(g,0,sizeof(g));
    memset(f,0,sizeof(f));
    scanf("%d",&n);
    scanf("%s",s);
    if (n>21) {printf("NO\n");return;}
    len=strlen(s);
    for (register int i=1;i<=21;i++) regis[i]=len+1;
    for (register int i=len-1;i>=0;i--)
    {
        for (register int j=1;j<=n;j++)
            g[i+1][j]=regis[j];
        regis[s[i]-‘a‘+1]=i+1;
    }
    for (register int i=1;i<=n;i++) g[0][i]=regis[i];
    for (register int i=1;i<=n;i++) g[len][i]=g[len+1][i]=len+1;
    int top=(1<<n)-1;
    for (register int i=0;i<=top;i++)
        for (register int j=1;j<=n;j++)
            if (i&(1<<(j-1)))
                f[i]=max(f[i],g[f[i^(1<<(j-1))]][j]);
    if (f[top]==len+1) printf("NO\n");
    else printf("YES\n");
}
int main()
{
    scanf("%d",&t);
    for (int i=1;i<=t;i++)
        work();
    return 0;
}
时间: 2024-08-27 15:27:50

BZOJ 4416 阶乘字符串的相关文章

BZOJ 4416 【SHOI2013】 阶乘字符串

题目链接:阶乘字符串 又是一道不会做的题--看了题解后我被吓傻了-- 首先我们可以有一个显然的\(O(2^nn)\)的做法.我们先预处理出\(g_{i,j}\)表示字符串中\(i\)号位置开始第一个\(j\)字符出现在什么位置.然后就可以用\(f_S\)表示使得\(S\)集合内字符的排列全都出现的最小长度,然后就可以递推了. 然后--翻了一波题解,发现当\(n>21\)的时候无解--听说合法的串长应该是\(n^2\)级别的,所以当\(n>21\)的时候就无解了--然后就可以\(O(2^nn)\

[SHOI2013]阶乘字符串

题目描述 给定一个由前\(n\)个小写字母组成的串\(S\). 串\(S\)是阶乘字符串当且仅当前\(n\)个小写字母的全排列(共\(n!\)种)都作为\(S\)的子序列(可以不连续)出现. 由这个定义出发,可以得到一个简单的枚举法去验证,但是它实在太慢了.所以现在请你设计一个算法,在\(1\)秒内判断出给定的串是否是阶乘字符串. 输入格式 输入第\(1\)行一个整数\(T\),表示这个文件中会有\(T\)组数据. 接下来分\(T\)个块,每块\(2\)行: 第\(1\)行一个正整数\(n\),

BZOJ 1090: [SCOI2003]字符串折叠( 区间dp )

按照题意dp...dp(l, r) = min{ dp(l, x) + dp(x+1, r) , 折叠(l, r) } 折叠(l, r)我是直接枚举长度然后哈希判.. -------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 109; con

BZOJ 1090 SCOI2003 字符串折叠 动态规划+Hash

题目大意:给定一个字符串,求按照题中所给的压缩方式最短能压缩到多长 区间DP 令f[i][j]表示[i,j]区间内的字符串最短能压缩到多长 普通的区间DP:f[i][j]=min{f[i][k]+f[k+1][j]} (i<=k<=j-1) 此外如果对这段字符串进行压缩,那么我们可以枚举循环节,用Hash来判断 如果k是一个循环节,那么有f[i][j]=min(f[i][j],f[i][i+k-1]+digit[len/k]+2) 其中len=j-i+1,digit表示一个数在十进制下的长度

BZOJ 1856 [Scoi2010]字符串 组合数

题意: 找出由n个1,m个0组成的字符串,且任意前几个字符中1的个数不能比0的个数少,询问满足要求的字符串个数. 解析: 很容易转化一下题意,转化到从一个矩阵的左下走到右上不能过某条线的方案数. 如果我们把1看作走一个向量(1,1),0看作走一个向量(1,-1),那么我们可以把模型转化成从(0,0)走到(n+m,n-m)并且不能经过直线y=-1的方案数. 暂且不考虑限制答案显然为C(n+m,m),如果考虑限制的话,我们看图发现经过y=-1的情况可以看作从(0,-2)出发到(n+m,n-m)的方案

大数据阶乘——字符串乘法器

char s[1001]; //字符串s存储乘法得到的大数字,s[0]代表低位 int GetLength()//返回s的最大不为空的元素下标 { int i; for(i=1000; i>=0; i--) { if(s[i]!='0') { break; } } return i; } void cal(int k)//计算大数字s和小数字k相乘 { int add=0;//进位数 int i; int temp; for(i=0; i<=GetLength(); i++)// { temp

bzoj 1090 [SCOI2003]字符串折叠(区间DP)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1090 [题意] 给定一个字符串,问将字符串折叠后的最小长度. [思路] 设f[i][j]表示将区间ij折叠后的最小长度,则有转移式: f[i][j]=min{ j-i+1,f[i][k]+f[k+1][j],f[i][i+x-1]+2+digit((j-i+1)/x) } 第一项代表不折叠,第二项代表当前不折叠,第三项代表以x长度折叠ij区间,条件是满足ij区间以x为循环节. [代码

bzoj4416: [Shoi2013]阶乘字符串

可以大胆猜想n>21时无解,至于依据,不开O2,1s,n<=21刚好能卡过去= = 并不会证= = #include<cstdio> void up(int& a,int b){ a=a<b?b:a; } int test,n,m,i,j; char t[500]; int f[1<<21],s[500][21]; int main(){ scanf("%d",&test); while(test--){ scanf("

bzoj 1856: [Scoi2010]字符串

1 #include<cstdio> 2 #include<iostream> 3 #define Q 20100403 4 long long n,m,a,b,k,ans=1; 5 int main() 6 { 7 scanf("%lld%lld",&n,&m); 8 b=(n-m+1)%Q; 9 for(int i=2;i<=m+n;i++) 10 b=(b*i)%Q; 11 a=1; 12 for(int i=2;i<=n+1;