\(\mathcal{Description}\)
题目大意 有一个\(r * c\)的矩阵上有\(n\)个点,问有多少个子矩阵里包含至少\(k\)个点
输入格式 第一行四个数\(r,c,n,k\),接下来\(n\)行,每行两个数\(x_i,y_i\),表示第\(i\)个点的坐标
输出格式 一行一个数表示合法子矩阵个数
数据范围 \(1\leq r,c,n\leq 3000 , 1\leq k\leq \min(n,10)\)
\(\mathcal{Solution}\)这题好难
题目要求至少包含了\(k\)个点的子矩阵,\(k\)很小
朴素的想法是枚举上边界,下边界,然后在中间尺取,这样是\(O\left(n^3\right)\)的
反过来考虑,从下往上枚举每一行
先算出以这一行的每个顶点为左上角,最下面一行为第\(c\)的所有合法矩阵个数
计算方法考虑尺取即可
再考虑最下面一行逐渐往上走的合法矩阵个数
这个过程中,点是在不断变少的
考虑算出将最下面一行变为其上面那一行时合法矩阵的减少量,再用原来的合法矩阵量减去就是最下面一行往上移一行后的合法矩阵数量
如何计算合法矩阵的减少量
枚举这一行中的每个点,计算出包含这个点且点数刚好为\(k\)的矩阵个数,然后删去这个点
计算方法也是尺取,另外,尺取时用一个链表表示该行中下一个点和上一个点的位置,这样尺取复杂度就是该行点数,而不是\(m\)
这样就能算出删去这一行中所有点后不合法的矩阵个数
那么往上移一行合法矩阵就会减去这些矩阵
如此计算,总复杂度为
\(O\left(n^2k+npk\right)\)
如有不懂之处,请参考代码
如仍有不懂之处可在评论提出,博主看到会给予回复
\(\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\n",ans); return 0; }
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧