1 条题解
-
0
注:下文所有的复杂度分析均认为 同阶。
思路
首先可以发现,最后被淹没的形状一定是一个矩形,否则一定存在一个点有两个相邻的被淹没的点。
考虑观察这个矩形有什么性质,发现矩形一定是被一圈 1 包起来的(但是外围的四个角可以不用管),且矩形内部不能存在一行或一列全为 1。
考虑设 表示满足 全部为 1 的最大的 , 表示满足 全部为 1 的最大的 。设包住矩形的左上角为 ,右下角为 ,上面的限制可以刻画为:
- ,加一的原因为右上角可以不为 1,后面所有加一的原因都是同理的;
- ;
- ;
- ;
- 不存在一个 使得 ;
- 不存在一个 使得 。
现在固定左上角,考虑从后两个限制入手。发现若一个 是合法的,下一个可能合法的 一定是在 右边第一个满足 的,下一个可能合法的 同理。而这个东西可以用单调栈维护。
接下来考虑前面四条限制,对于一个 ,可能合法的 一定是第一个满足 的,所以可以在单调栈上使用双指针找到这个 ,然后再判断其他的条件即可(此时一定满足第 3 条和第 5 条限制,前 4 条限制是好判的,第 6 条限制可以通过判断扫到的上一个 是否会延伸到 以下)。
分析一下每一行会扫到的 的量级,发现对于 的 扫到一个后剩下的一定不会合法, 的 扫到后就会被弹出单调栈, 的量级也是同理的。所以合法的矩形个数是 的,可以被接受。
接下来考虑统计答案,对于每个点,最后形成的联通块相当于覆盖到这个点面积最小的矩形。此时做法就很多了,可以使用扫描线+标记永久化线段树,每个线段树节点维护一个可删堆即可。
时间复杂度 ,实际上根本跑不满,因为要卡满合法矩形的个数矩形的几乎不会有交,堆中元素很少,否则个数不大运算量也会变小。
但是这么写有概率会被卡常,对于面积和小的跑暴力,面积和大的跑扫描线。代码
#include<bits/stdc++.h> using namespace std; const int N = 5005; int n,m,dn[N][N],rt[N][N],stk[N][N],top[N]; int l1[N*N],r1[N*N],l2[N*N],r2[N*N],s[N*N],cnt; long long ans = 0; char ch[N][N]; namespace sub1{ int id[N*N],a[N][N]; inline void main() { for(int i = 1;i<=cnt;i++) id[i] = i; sort(id+1,id+cnt+1,[&](int x,int y){return s[x]>s[y];}); for(int i = 1;i<=cnt;i++) { int now = id[i]; for(int x = l1[now];x<=r1[now];x++) for(int y = l2[now];y<=r2[now];y++) a[x][y] = s[now]; } for(int i = 1;i<=n;i++) for(int j = 1;j<=m;j++) if(ch[i][j]=='0') ans+=a[i][j]; cout<<ans; } } namespace sub2{ vector<pair<pair<int,int>,int>> vec[N]; priority_queue<int> q[N<<2],del[N<<2]; #define ls (k<<1) #define rs (k<<1|1) void change(int k,int l,int r,int x,int y,int v) { if(l>y||r<x) return; if(l>=x&&r<=y) { if(v<0) { del[k].push(v); while(!q[k].empty()&&!del[k].empty()&&q[k].top()==del[k].top()) q[k].pop(),del[k].pop(); } else q[k].push(-v); return; } int mid = (l+r)/2; change(ls,l,mid,x,y,v),change(rs,mid+1,r,x,y,v); } void ask(int k,int l,int r,int x,int mn) { if(!q[k].empty()) mn = min(mn,-q[k].top()); if(l==r) { if(ch[x][l]=='0') ans+=mn; return; } int mid = (l+r)/2; ask(ls,l,mid,x,mn),ask(rs,mid+1,r,x,mn); } inline void main() { for(int i = 1;i<=cnt;i++) { vec[l1[i]].push_back({{l2[i],r2[i]},s[i]}); vec[r1[i]+1].push_back({{l2[i],r2[i]},-s[i]}); } for(int i = 2;i<n;i++) { for(auto _:vec[i]) { int l = _.first.first,r = _.first.second,v = _.second; change(1,2,m-1,l,r,v); } ask(1,2,m-1,i,2e9); } cout<<ans; } } signed main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m; n+=2,m+=2; for(int i = 2;i<n;i++) for(int j = 2;j<m;j++) cin>>ch[i][j]; for(int i = 1;i<=n;i++) ch[i][1] = ch[i][m] = '1'; for(int i = 1;i<=m;i++) ch[1][i] = ch[n][i] = '1'; for(int i = n;i;i--) for(int j = m;j;j--) { if(ch[i][j]=='1') { if(ch[i+1][j]=='1') dn[i][j] = dn[i+1][j]; else dn[i][j] = i; if(ch[i][j+1]=='1') rt[i][j] = rt[i][j+1]; else rt[i][j] = j; } else { if(ch[i+1][j]=='1') dn[i][j] = dn[i+1][j]; if(ch[i][j+1]=='1') rt[i][j] = rt[i][j+1]; } } long long sum = 0; for(int i = n;i;i--) { top[0] = 0; for(int j = m;j;j--) { int las1 = 0; while(top[0]) { int now = stk[0][top[0]]; while(top[j]) { int k = stk[j][top[j]]; if(rt[k][j]>rt[i][j]||rt[k][j]+1>=now) break; top[j]--; } int k = stk[j][top[j]]; if(top[j]&&now<=min(rt[i][j],rt[k][j])+1&&k<=min(dn[i][j],dn[i][now])+1&&k>dn[i][las1]) { if(i+1<k&&j+1<now) { int w = (k-i-1)*(now-j-1); sum+=w; cnt++; s[cnt] = w,l1[cnt] = i+1,r1[cnt] = k-1,l2[cnt] = j+1,r2[cnt] = now-1; } } if(dn[i][now]>dn[i][j]) break; las1 = stk[0][top[0]]; top[0]--; } stk[0][++top[0]] = j; while(top[j]&&rt[stk[j][top[j]]][j]<=rt[i][j]) top[j]--; stk[j][++top[j]] = i; } } if(sum<=3e8) sub1::main(); else sub2::main(); return 0; }
- 1
信息
- ID
- 10209
- 时间
- 3000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者