• CF627E Orchestra [矩阵计数]


    也许更好的阅读体验

    (mathcal{Description})

    题目大意 有一个(r * c)的矩阵上有(n)个点,问有多少个子矩阵里包含至少(k)个点

    输入格式 第一行四个数(r,c,n,k),接下来(n)行,每行两个数(x_i,y_i),表示第(i)个点的坐标

    输出格式 一行一个数表示合法子矩阵个数

    数据范围 (1leq r,c,nleq 3000 , 1leq kleq min(n,10))

    (mathcal{Solution})
    这题好难

    题目要求至少包含了(k)个点的子矩阵,(k)很小
    朴素的想法是枚举上边界,下边界,然后在中间尺取,这样是(Oleft(n^3 ight))

    反过来考虑,从下往上枚举每一行
    先算出以这一行的每个顶点为左上角,最下面一行为第(c)的所有合法矩阵个数
    计算方法考虑尺取即可
    再考虑最下面一行逐渐往上走的合法矩阵个数

    这个过程中,点是在不断变少的
    考虑算出将最下面一行变为其上面那一行时合法矩阵的减少量,再用原来的合法矩阵量减去,得到最下面一行往上移一行后的合法矩阵数量

    如何计算合法矩阵的减少量
    枚举这一行中的每个点,计算出包含这个点且点数刚好为(k)的矩阵个数,然后删去这个点
    计算方法也是尺取,另外,尺取时用一个链表表示该行中下一个点和上一个点的位置,这样尺取复杂度就是该行点数,而不是(m)
    这样就能算出删去这一行中所有点后不合法的矩阵个数
    那么往上移一行合法矩阵就会减去这些矩阵

    如此计算,总复杂度为
    (Oleft(n^2k+npk ight))

    如有不懂之处,请参考代码
    如仍有不懂之处可在评论提出,博主看到会给予回复
    (mathcal{Code})

    /*******************************
    Author:Morning_Glory
    LANG:C++
    Created Time:2019年10月15日 星期二 15时37分21秒
    *******************************/
    #include <cstdio>
    #include <fstream>
    #include <vector>
    #define ll long long
    using namespace std;
    const int maxn = 3005;
    int n,m,p,k;
    int s[maxn],t[maxn],last[maxn],nxt[maxn];
    ll ans;
    vector <int> dot[maxn];
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&p,&k);//求n行m列p个点至少包含k个点的矩阵个数
    	for (int i=1;i<=p;++i){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		dot[x].push_back(y);//每行的点
    	}
    
    	for (int u=n;u>=1;--u){
    		for (int i=0;i<dot[u].size();++i)	++s[dot[u][i]];//s[i] -> 第n行到第u行上第i列的点数
    		int l=1,r=0,num=0;
    		ll inc=0;
    		while (true){
    			while (r+1<=m&&num<k)	num+=s[++r];
    			if (num<k)	break;
    			while (l<=r&&num>=k)	num-=s[l++],inc+=m-r+1;//inc 合法矩阵个数
    		}
    
    		for (int i=1;i<=m;++i)	t[i]=s[i];
    		for (int i=0;i<=m+1;++i)	last[i]=i-1,nxt[i]=i+1;
    		for (int i=1;i<=m;++i)
    			if (!s[i])	last[nxt[i]]=last[i],nxt[last[i]]=nxt[i];
    
    		for (int d=n;d>=u;--d){
    			ans+=inc;
    
    			for (int i=0;i<dot[d].size();++i){
    
    				int cur=dot[d][i],sl=t[cur],sr=0;
    				l=r=cur;
    				while (nxt[r]<=m&&sl+sr+t[nxt[r]]<=k)	sr+=t[r=nxt[r]];
    
    				while (true){
    					if (sl+sr==k)	inc-=(l-last[l])*(nxt[r]-r);
    					sl+=t[l=last[l]];
    					if (!l||sl>k)	break;
    					while (sl+sr>k)	sr-=t[r],r=last[r];
    				}
    
    				--t[cur];
    				if (!t[cur])	last[nxt[cur]]=last[cur],nxt[last[cur]]=nxt[cur];
    			}
    		}
    	}
    
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    

    如有哪里讲得不是很明白或是有错误,欢迎指正
    如您喜欢的话不妨点个赞收藏一下吧

  • 相关阅读:
    Codeforces Round #636 D. Constant Palindrome Sum(差分/好题)
    Codeforces Round #636 C. Alternating Subsequence
    Codeforces Round #636 B. Balanced Array(水)
    Codeforces Round #636 A. Candies(水)
    洛谷P2136 拉近距离(负环判定)
    P2850 [USACO06DEC]Wormholes G(负环判定)
    架构--缓存知识
    集群-架构
    ELK-第二集
    Linux下的I/O复用与epoll详解
  • 原文地址:https://www.cnblogs.com/Morning-Glory/p/11681121.html
Copyright © 2020-2023  润新知