【AGC048F/nflsoj#855/floj#3297】 序列
Description
- 传送门(atcoder)
- (nle 300)
Solution
为什么这题题号这么多?当然是因为出题人大胆搬AGC原题...
官方题解在此,写的很好,可惜是英文,以下内容大概是翻译自原题解并加上了自己的理解。
首先将序列翻转,将操作转化为每次取出形如(“10101010”)的序列。考虑这样一种情况,我们每次都贪心地取出最长的可以取出的序列,记它们的长度为(l_1,l_2dots l_n),显然这样做是取出元素最多的情况,如果这样都无法取完,那么一定无解。
我们将(A)中的元素从大到小排序为(x_1,x_2dots x_m),那么可以证明(A)可以被得到当且仅当以下三个条件同时满足:
(1.)(sum l_i=sum x_i)
(2.)对于(forall jin{1,m}),有(sum_{i=1}^{j} lceil frac{l_i}{2} ceil=sum_{i=1}^{j} lceil frac{x_i}{2} ceil),这个代表前(j)个序列中(1)的个数
(3.)对于(forall jin{1,m}),有(sum_{i=1}^{j} lfloor frac{l_i}{2} floor=sum_{i=1}^{j} lfloorfrac{x_i}{2} floor),这个代表前(j)个序列中(0)的个数
对于必要性,第(1)条左右两边均表示原序列长度,必然成立,对于第(2,3)条,每一个(x_i)都表示一个形如(“10101010”)的序列,而选出前(j)个(l_i)一定能是(1)和(0)的个数最大。
对于充分性,我们待会再证明。现在有了这三条条件我们就能进行(DP),设状态(f[i][j][k][w])表示已经考虑了前(i)个(x),(x_i=j),(sum_{d=1}^{i} lceil frac{x_d}{2} ceil=k),(sum_{d=1}^{i} lfloor frac{x_d}{2} floor=w),因为(x[i]le frac{n}{i}),于是总状态数是(mathcal O(n^3log(n)))的,转移方程如下:
于是可以前缀和优化达到时间复杂度(mathcal O(n^3log(n))),空间复杂度也能通过滚动数组优化到(mathcal O(n^3))。
回过头来证明充分性,首先给出一个引理:
引理:定义形如(“1010101")的序列为好串。对于两个好串(a)与(b)((|a|le |b|)),一定能将(a)与(b)的字符全部取出来打乱顺序后重新拆分得到字符串(c)与(d)满足:
[|a|le |c|le |d|le |b| ]感性理解:假设将(a)与(b)的字符全部取出来后组成了新字符串(w),在(w)中取出最长的好串(p),那么剩下的字符一定也是一个好串,我们称为(q)。必然有(|b|le |p|),假设(p)和(q)是通过一开始我们提到的中贪心方法选择的,那么我们一定可以通过重新安排它们中的元素得到(c)与(d)。
现在考虑我们需要通过某种方法重新安排(l)中的元素使它们变成(x),显然由(nle m),因此我们在(l)后加几个(0)使(l)的长度也变为(m)。
- 首先我们寻找到一个(i)满足(l_i<x_i),如果不满足,那么可以推出(l=x),于是工作直接完成。
- 找到(i)后,如果存在(j)满足(j<i)且(l_jge x_j+2),那么有(l_i< x_ile x_jle l_j-2),于是根据上面的引理,我们可以将(l_j)与(l_i)重组为(l_j-2)与(l_i+2)。
- 否则,说明一定存在一个(j)满足(j<i)且(l_j=x_j+1),否则无法满足性质(2)与(3),因此(l_i<x_ile x_jle l_j-1),我们可以将(l_j)与(l_i)重组为(l_j-1)与(l_i+1)
- 反复寻找(i),最终我们一定会成功将(l)转化为(x)
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=310;
const int mod=1e9+7;
#define it set<int>::iterator
char ch[N];
int l[N],a[N],n,tot,d[2][N],sum[N];
inline void pre(set<int> s){
if(!s.size()) return ;
int op=1,len=0;
set<int> nxt;
it now=s.begin();
while(now!=s.end()){
if(a[*now]==op) len++,op^=1;
else nxt.insert(*now);
now++;
}
if(!len) return ;
l[++tot]=len;
pre(nxt);
}
ll f[2][N][N][N];
inline void upd(int &x,int y){x=x+y;}
signed main(){
scanf("%s",ch+1);
n=strlen(ch+1);
reverse(ch+1,ch+n+1);
set<int> s;
for(int i=1;i<=n;++i) s.insert(i),a[i]=ch[i]-'0';
pre(s);
for(int i=1;i<=n;++i) d[0][i]=d[0][i-1]+l[i]/2,d[1][i]=d[1][i-1]+(l[i]+1)/2;
if(d[0][n]+d[1][n]<n){puts("0");return 0;}
for(int i=0;i<=n;++i) f[0][i][0][0]=1;
for(int i=1;i<=n;++i){
int cur=i&1,last=!cur,d0=d[0][i],d1=d[1][i];
for(int j=0;j*i<=n&&j<=n;++j)
for(int k=0;k<=d0;++k)
for(int w=0;w<=d1;++w)
f[cur][j][k][w]=f[last][j][k-j/2][w-(j+1)/2];
for(int j=n/i-1;j>=0;--j){
for(int k=0;k<=d0;++k)
for(int w=0;w<=d1;++w)
f[cur][j][k][w]+=f[cur][j+1][k][w];
}
}
printf("%d
",f[n&1][0][d[0][n]][d[1][n]]%mod);
return 0;
}