博客
关于我
[ARC100-F][DP]Colorful Sequences
阅读量:92 次
发布时间:2019-02-26

本文共 3200 字,大约阅读时间需要 10 分钟。

翻译

给你 K K K m m m,给出一个长度为 m m m的由 [ 1 , K ] [1,K] [1,K]组成的序列

问用 [ 1 , K ] [1,K] [1,K]组成的长度为 n n n的好序列中有多少个如上给出的序列
定义一个序列为好序列当且仅当其有一个长度为 K K K的子串,满足 [ 1 , K ] [1,K] [1,K]在其中各出现了一次

题解

gank英文题解现场

begay的题解太难懂了…
正难则反
考虑用在所有序列中的数量减去在非法序列中的情况
第一个答案就是 K n − m ( n − m + 1 ) K^{n-m}(n-m+1) Knm(nm+1)
第二种答案我们分情况讨论
1:若给出的序列是个好序列,此时不存在他在非法序列中的情况,直接输出即可
对于以下两种情况,先考虑一个子问题的求法
如何求长度为 i i i,末尾存在长度为 j j j的序列满足其中的数两两不同,但 j + 1 j+1 j+1就存在相同了的序列个数
d p [ i ] [ j ] dp[i][j] dp[i][j]表示如上,考虑转移至 d p [ i + 1 ] [ k ] dp[i+1][k] dp[i+1][k]
k ≤ j k\leq j kj,显然贡献仅有 1 1 1
k = j + 1 k=j+1 k=j+1,显然我们后面能填的数有 K − j + 1 K-j+1 Kj+1个,贡献为 K − j + 1 K-j+1 Kj+1
其余贡献为 0 0 0
转移用一个前缀和优化掉即可
2:给出的序列满足数两两不同
此时求一个问题的答案,即所有非法序列中长度为 m m m的满足两两不同的序列个数和
出来之后除掉一个 k ! ( k − m ) ! \frac{k!}{(k-m)!} (km)!k!即可
这个问题可以用上面的 d p dp dp完成,注意记录一个 g [ i ] [ j ] g[i][j] g[i][j]表示长度为 m m m的序列数
3:剩余情况
我们求出其前缀最长有多长满足两两不同,后缀最长有多长满足两两不同
然后可以发现的是,我们前面填的东西与后面填的东西是完全不影响的
那么就枚举一下串的位置,用上面的 d p dp dp数组计数即可
具体可以看代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long#define mp(x,y) make_pair(x,y)#define pll pair
#define pii pair
using namespace std;inline int read(){ int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar();} return x*f;}int stack[20];inline void write(int x){ if(x<0){ putchar('-');x=-x;} if(!x){ putchar('0');return;} int top=0; while(x)stack[++top]=x%10,x/=10; while(top)putchar(stack[top--]+'0');}inline void pr1(int x){ write(x);putchar(' ');}inline void pr2(int x){ write(x);putchar('\n');}const int MAXN=25005;const int MAXM=405;const int mod=1e9+7;int pow_mod(int a,int b){ int ret=1; while(b) { if(b&1)ret=1LL*ret*a%mod; a=1LL*a*a%mod;b>>=1; } return ret;}int f[MAXN][MAXM],s[MAXM],s1[MAXM],g[MAXN][MAXM];int n,K,m,a[MAXN];int pre[MAXN],inv[MAXN];LL ans;bool check1(){ int cnt=0; if(m
K)return false; memset(s,0,sizeof(s)); for(int i=1;i<=m;i++) { s[a[i]]++; if(s[a[i]]>1)return false; } return true;}void ad(int &x,int y){ x+=y;if(x>=mod)x-=mod;}int C(int n,int m){ return 1LL*pre[n]*inv[m]%mod*inv[n-m]%mod;}void solve2(){ memset(s,0,sizeof(s)); s[0]=s1[0]=f[0][0]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=min(i,K);j++) { ad(f[i][j],s[j]);ad(f[i][j],1LL*f[i-1][j-1]*(K-j+1)%mod); ad(g[i][j],s1[j]); ad(g[i][j],1LL*g[i-1][j-1]*(K-j+1)%mod); if(j>=m)ad(g[i][j],f[i][j]); } for(int j=K-1;j>=0;j--)s[j]=(s[j+1]+f[i][j])%mod,s1[j]=(s1[j+1]+g[i][j])%mod; } int sum=0; for(int i=1;i
1){ ln1=i-1;break;} } memset(s,0,sizeof(s)); for(int i=m;i>=1;i--) { s[a[i]]++; if(s[a[i]]>1){ ln2=m-i;break;} } int sum=0; for(int i=ln1;i-ln1+1+m-1<=n;i++) { int s1=0,s2=0; for(int j=ln1;j
=0;i--)inv[i]=1LL*inv[i+1]*(i+1)%mod; n=read();K=read();m=read(); for(int i=1;i<=m;i++)a[i]=read(); ans=1LL*pow_mod(K,n-m)*(n-m+1)%mod; if(check1())return pr2(ans),0;//colorful if(check2())solve2(); else { memset(s,0,sizeof(s)); s[0]=f[0][0]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=min(i,K);j++) { ad(f[i][j],s[j]); ad(f[i][j],1LL*f[i-1][j-1]*(K-j+1)%mod); } s[K]=0; for(int j=K-1;j>=0;j--)s[j]=(s[j+1]+f[i][j])%mod; } solve3(); } return 0;}

转载地址:http://gemu.baihongyu.com/

你可能感兴趣的文章
MySQL 快速创建千万级测试数据
查看>>
mysql 快速自增假数据, 新增假数据,mysql自增假数据
查看>>
MySql 手动执行主从备份
查看>>
Mysql 批量修改四种方式效率对比(一)
查看>>
Mysql 报错 Field 'id' doesn't have a default value
查看>>
MySQL 报错:Duplicate entry 'xxx' for key 'UNIQ_XXXX'
查看>>
Mysql 拼接多个字段作为查询条件查询方法
查看>>
mysql 排序id_mysql如何按特定id排序
查看>>
Mysql 提示:Communication link failure
查看>>
mysql 插入是否成功_PDO mysql:如何知道插入是否成功
查看>>
Mysql 数据库InnoDB存储引擎中主要组件的刷新清理条件:脏页、RedoLog重做日志、Insert Buffer或ChangeBuffer、Undo Log
查看>>
mysql 数据库中 count(*),count(1),count(列名)区别和效率问题
查看>>
mysql 数据库备份及ibdata1的瘦身
查看>>
MySQL 数据库备份种类以及常用备份工具汇总
查看>>
mysql 数据库存储引擎怎么选择?快来看看性能测试吧
查看>>
MySQL 数据库操作指南:学习如何使用 Python 进行增删改查操作
查看>>
MySQL 数据库的高可用性分析
查看>>
MySQL 数据库设计总结
查看>>
Mysql 数据库重置ID排序
查看>>
Mysql 数据类型一日期
查看>>