zoukankan      html  css  js  c++  java
  • Atcoder Grand Contest 033 D Complexity(dp)

    Atcoder 题面传送门 & 洛谷题面传送门

    首先 \(n^5\) 的暴力非常容易想,设 \(dp_{a,b,c,d}\) 表示以 \((a,b)\) 为左上角,\((c,d)\) 为右下角的矩阵的 complexity。枚举断点转移即可,时间复杂度 \(n^5\)

    我们考虑优化这个 \(dp\),首先比较明显的一点:这个 \(dp\) 状态满足单调性,也就是说 \(\forall d_1<d_2,dp_{a,b,c,d_1}\le dp_{a,b,c,d_2}\),也就是说对于某个 \(dp\) 状态 \(dp_{a,b,c,d}\),假设最大的满足 \(dp_{a,b,c,k}<dp_{a,b,k+1,d}\)\(k\)\(r\),那么我们从行处切开的决策点 \(k\) 要么为 \(r\),要么为 \(r+1\),这个异常好想,于是我们可以二分这个 \(r\) 然后从 \(r\)\(r+1\) 转移即可,时间复杂度 \(n^4\log n\),用双针也可优化到 \(n^4\)

    不过仅仅进行这个优化是远远不够的,我们还需进一步优化。这一步就有亿点考验观察力了,不难发现这个答案不会太大,手玩几组数据可看出这个答案最大不过 \(\log n\) 级别(当然如果硬要说理的话用二维线段树的思想也可以解释),也就是说我们 \(dp\) 数组的值域最大大概在 \(16\) 左右。所以我们可以很自然地想到一个类似于函数里面“交换定义域和值域”的思路:将答案放入 \(dp\) 状态中。

    我们重新设计 \(dp\) 状态。设 \(f_{x,l,r,c}\) 表示最大的 \(y\) 满足 \(dp_{x,y,l,r}\leq c\)。转移分两种情况,横着切或者竖着切。如果我们横着切,那么我们肯定会贪心地选择最大的 \(dp_{x,y,l,r}\leq c-1\)\(y\) 作为分割点,也就是说 \(f_{x,l,r,c}\leftarrow f_{f_{x,l,r,c-1}+1,l,r,c}\)。如果我们竖着切,考虑枚举断点 \(k\) 并从 \(k\)\(k+1\) 之间切开,那么 \(dp_{x,y,l,r}\leq c\) 就意味着 \(dp_{x,y,l,k}\leq c-1,dp_{x,y,k+1,r}\leq c-1\),故我们用 \(\min(f_{x,l,k,c-1},f_{x,k+1,r,c-1})\) 更新 \(f_{x,l,r,c}\)。这样暴力更新还是 \(n^4\log n\) 的。不过还是按照之前的套路,由 \(dp\) 数组满足单调性也可推出 \(f\) 数组也满足单调性,即 \(\forall r_1<r_2,f_{x,l,r_1,c}\ge f_{x,l,r_2,c}\),于是我们二分断点转移即可,复杂度就降到了 \(n^3\log^2n\)

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define ppb pop_back
    #define mp make_pair
    template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
    template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef unsigned int u32;
    typedef unsigned long long u64;
    namespace fastio{
    	#define FILE_SIZE 1<<23
    	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
    	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
    	inline void putc(char x){(*p3++=x);}
    	template<typename T> void read(T &x){
    		x=0;char c=getchar();T neg=0;
    		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
    		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    		if(neg) x=(~x)+1;
    	}
    	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
    	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
    	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
    }
    const int MAXN=185;
    const int MAXANS=17;
    int n,m,sum[MAXN+5][MAXN+5];char s[MAXN+5][MAXN+5];
    int f[MAXN+5][MAXN+5][MAXN+5][MAXANS+1];
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
    		sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(s[i][j]=='.');
    	}
    	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=j;k<=m;k++){
    		int l=i,r=n,p=i-1;
    		while(l<=r){
    			int mid=l+r>>1,ss=sum[mid][k]-sum[mid][j-1]-sum[i-1][k]+sum[i-1][j-1];
    			if(ss==0||ss==(mid-i+1)*(k-j+1)) p=mid,l=mid+1;
    			else r=mid-1;
    		} f[i][j][k][0]=p;
    	}
    	for(int p=1;p<=MAXANS;p++) for(int i=1;i<=n;i++) for(int len=1;len<=m;len++)
    		for(int j1=1,j2=len;j2<=m;j1++,j2++){
    			f[i][j1][j2][p]=f[i][j1][j2][p-1];
    			chkmax(f[i][j1][j2][p],f[f[i][j1][j2][p-1]+1][j1][j2][p-1]);
    			if(f[i][j1][j2][p]==n) continue;
    			int l=j1,r=j2-1;
    			while(l<=r){
    				int mid=l+r>>1;
    				chkmax(f[i][j1][j2][p],min(f[i][j1][mid][p-1],f[i][mid+1][j2][p-1]));
    				if(f[i][j1][mid][p-1]<f[i][mid+1][j2][p-1]) r=mid-1;
    				else l=mid+1;
    			}
    		}
    	int ans=-1;
    	for(int i=0;i<=MAXANS;i++) if(f[1][1][m][i]<n) chkmax(ans,i);
    	printf("%d\n",ans+1);
    	return 0;
    }
    
  • 相关阅读:
    Android Studio 快捷键(包含自定义)终极版
    C#开发模式——dll多级引用的问题
    WPF实现QQ群文件列表动画(二)
    WPF实现QQ群文件列表动画(一)
    VS2010Datatable查看器查看超时(Microsoft.VisualStudio.DebuggerVisualizers)
    WPF里ItemsControl的分组实现
    WPF触控程序开发(四)——MultiTouchVista_-_second_release_-_refresh_2的救赎
    WPF异步回调时回调函数如何获取异步函数产生的变量
    裸奔着造房子——对政府禁止采购Win8系统的一些看法
    全球征集-如何实现回文SQL的查询
  • 原文地址:https://www.cnblogs.com/ET2006/p/agc033D.html
Copyright © 2011-2022 走看看