zoukankan      html  css  js  c++  java
  • 【AGC048F】【2020六校联考WC #9】序列【构造】【DP】

    【AGC048F/nflsoj#855/floj#3297】 序列

    Description

    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)))的,转移方程如下:

    [f[i][j][k][w]=sum_{tge j}f[i-1][t][k-lceilfrac j2 ceil][w-lfloorfrac j2 floor] ]

    于是可以前缀和优化达到时间复杂度(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;
    }
    
  • 相关阅读:
    游戏开发中——垂直同步、绘制效率、显示器刷新频率与帧率
    python 异常
    python 多文件知识
    python if,for,while
    python 算术运算
    1.英语单词笔记
    Java import的作用
    java基础点
    Eclipse Java注释模板设置详解
    Java文档注释详解
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/14327539.html
Copyright © 2011-2022 走看看