• 【洛谷3474】[POI2008] KUP-Plot purchase(悬线法)


    题目链接

    • 给定一个 (n imes n) 的矩阵和一个整数 (k),要求找出任意一个元素和在 ([k,2k]) 范围内的子矩阵,或判断不存在。
    • (1le nle2 imes10^3)(1le kle10^9)(0le a_{i,j}le2 imes10^9)

    性质讨论

    显然,如果子矩阵中包含大于(2k) 的元素,这个子矩阵的元素和肯定大于 (2k)

    而如果我们想在一个所有元素都小于等于 (2k) 的子矩阵中寻找一个元素和在 ([k,2k]) 范围内的子矩阵,假设元素和为 (S),根据 (S) 的大小讨论:

    • (S < k):显然无解。
    • (kle Sle 2k):这个子矩阵就是答案。
    • (S>2k):不断将它拆分,则总有一个时刻会拆出两个小于等于 (2k) 的子矩阵,其中一个大于等于 (k),一个小于 (k),大于等于 (k) 且小于等于 (2k) 的那个可以作为答案。

    综上,只要一个子矩阵中没有大于 (2k) 的元素且元素和大于等于 (k),就可以从中找出答案。

    实际上我们只要找出所有没有大于 (2k) 的元素的极大子矩阵,如果它的元素和大于等于 (k) 就可以找出答案,否则无解。

    悬线法找极大子矩阵

    悬线法是在有障碍点的情况下求极大子矩阵的一个算法。

    对于每个位置我们预处理出它能到的最上面的一行。

    显然极大子矩阵中至少有一列顶到了它能到的最上面的一行,且它无法再向两侧扩展。

    也就是说,我们可以去枚举每一行,对于这行的每个位置,利用单调栈求出选中了 从这个位置到它能到的最上面的位置(悬线)后,向左向右分别能扩展到哪一列。

    一旦找到一个元素和大于等于 (k) 的子矩阵就可以去寻找答案了。

    代码:(O(n^2))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define LL long long
    #define N 2000
    #define S(A,B,C,D) (s[C][D]-s[(A)-1][D]-s[C][(B)-1]+s[(A)-1][(B)-1])
    using namespace std;
    int n,k,St[N+5],L[N+5],R[N+5],a[N+5][N+5],h[N+5][N+5];LL s[N+5][N+5];
    I void Solve(CI A,CI B,CI C,CI D)//在(A,B)-(C,D)这个子矩阵中找答案
    {
    	RI t=S(A,B,C,D);if(t<k) return;if(t<=2*k) printf("%d %d %d %d
    ",A,B,C,D),exit(0);
    	if(A^C) {RI m=A+C>>1;return Solve(A,B,m,D),Solve(m+1,B,C,D);}RI m=B+D>>1;return Solve(A,B,C,m),Solve(A,m+1,C,D);//分割,实际上可以一行一列地删
    }
    int main()
    {
    	RI i,j;for(scanf("%d%d",&k,&n),i=1;i<=n;++i) for(j=1;j<=n;++j) scanf("%d",&a[j][i]);
    	for(i=1;i<=n;++i) for(j=1;j<=n;++j) s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];//二维前缀和
    	for(j=1;j<=n;++j) h[0][j]=1;for(i=1;i<=n;++i) for(j=1;j<=n;++j) h[i][j]=a[i][j]<=2*k?h[i-1][j]:i+1;//预处理每个位置能到的最上面一行
    	RI T;for(i=1;i<=n;++i)//枚举每一行
    	{
    		for(h[i][St[T=1]=0]=n+2,j=1;j<=n;L[j]=St[T]+1,St[++T]=j++) W(h[i][St[T]]<=h[i][j]) --T;//求出每根悬线向左最多到哪一列
    		for(h[i][St[T=1]=n+1]=n+2,j=n;j;R[j]=St[T]-1,St[++T]=j--) W(h[i][St[T]]<=h[i][j]) --T;//求出每根悬线向右最多到哪一列
    		for(j=1;j<=n;++j) S(h[i][j],L[j],i,R[j])>=k&&(Solve(h[i][j],L[j],i,R[j]),0);//判断是否存在元素和大于k的极大子矩阵
    	}return puts("NIE"),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    JQuery中的动画
    javascript之变量、作用域、作用域链
    正确理解javascript的this关键字
    我忽略了的DOCTYPE!
    JQuery中的DOM操作
    发布一款html5移动端scroll框架:xScroll
    在debug时使Flutter中的print打印json数据时更美观易读
    使用ValueListenableBuilder监听TextEditingController
    Flutter使用rxdart和ChangeNotifier实现的倒计时按钮
    Flutter设计一个长按自动步进的按钮
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3474.html
Copyright © 2020-2023  润新知