【AGC048F】【2020六校联考WC #9】序列【构造】【DP】
【AGC048F/nflsoj#855/floj#3297】 序列
Description
- 传送门(atcoder)
- \(n\le 300\)
Solution
为什么这题题号这么多?当然是因为出题人大胆搬AGC原题...
官方题解在此,写的很好,可惜是英文,以下内容大概是翻译自原题解并加上了自己的理解。
首先将序列翻转,将操作转化为每次取出形如\(“10101010”\)的序列。考虑这样一种情况,我们每次都贪心地取出最长的可以取出的序列,记它们的长度为\(l_1,l_2\dots l_n\),显然这样做是取出元素最多的情况,如果这样都无法取完,那么一定无解。
我们将\(A\)中的元素从大到小排序为\(x_1,x_2\dots x_m\),那么可以证明\(A\)可以被得到当且仅当以下三个条件同时满足:
\(1.\)\(\sum l_i=\sum x_i\)
\(2.\)对于\(\forall j\in\{1,m\}\),有\(\sum_{i=1}^{j} \lceil \frac{l_i}{2}\rceil=\sum_{i=1}^{j} \lceil \frac{x_i}{2}\rceil\),这个代表前\(j\)个序列中\(1\)的个数
\(3.\)对于\(\forall j\in\{1,m\}\),有\(\sum_{i=1}^{j} \lfloor \frac{l_i}{2}\rfloor=\sum_{i=1}^{j} \lfloor\frac{x_i}{2}\rfloor\),这个代表前\(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}\rceil=k\),\(\sum_{d=1}^{i} \lfloor \frac{x_d}{2}\rfloor=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\),显然由\(n\le m\),因此我们在\(l\)后加几个\(0\)使\(l\)的长度也变为\(m\)。
- 首先我们寻找到一个\(i\)满足\(l_i<x_i\),如果不满足,那么可以推出\(l=x\),于是工作直接完成。
- 找到\(i\)后,如果存在\(j\)满足\(j<i\)且\(l_j\ge x_j+2\),那么有\(l_i< x_i\le x_j\le 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_i\le x_j\le 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\n",f[n&1][0][d[0][n]][d[1][n]]%mod);
return 0;
}