【题解】CF1534F2 Falling Sand (Hard Version)

Easy Version

考虑对于 (i,j) 向上下左右连出的 有向边 。缩点后答案就是 in[i]=0 的个数。

Hard Version

由于不需要全部消除,所以贪心地全部消完是不可行的。

trick1: 对于这个图,我们只需要让第 i 行第 a[i] 块掉下即可,我们称其为特殊点;

trick2: 观察到图的特征是仅向相邻的列连边,所以猜测选取某一个点消除的影响是 [l,r]

观察:

3 6 ..#..# ...... #####. 1 1 2 1 1 1

如果特殊点 X 可以到 Y 的话,我们将 Y 删掉,因为只需要得到 X 即可。这样将剩下的点按从左到右的顺序重新编号,可以证明选取图中某一节点,会覆盖 新编号节点中的一段连续区间 。这样在缩点后的 DAGDP ,只需要记录最左和最右节点,做一次区间覆盖即可。

证明:假设能得到特殊点 i<j<k ,其中 j 不可达,那么一定和 j 有交点。如果是在下方,则从特殊点 j 能到达特殊点 i,k ,要么是缩点后在同一节点上,不难证明缩点后的节点代表的特殊点也是一段连续区间;否则与上文矛盾。如果是在上方,那么可以到达特殊点 j ,证毕。

#include<bits/stdc++.h> #define fi first #define se second #define ll long long #define PII pair<int,int> #define All(x) x.begin(),x.end() #define INF 0x3f3f3f3f using namespace std; const int mx=4e5+5; int n,m,cnt,low[mx],dfn[mx],num,bl,c[mx],vis[mx],in[mx],a[mx],b[mx],l[mx],r[mx],siz,marked[mx],idx; PII d[mx]; queue<int> q; stack<int> st; set<PII> s[mx]; vector<PII> s2[mx]; vector<PII> v,e; vector<int> sp; vector<int> g[mx],g2[mx]; void tarjan(int x) { low[x]=dfn[x]=++num; vis[x]=1,st.push(x); for(auto y:g[x]) { if(!dfn[y]) { tarjan(y),low[x]=min(low[x],low[y]); } else if(vis[y]){ low[x]=min(low[x],dfn[y]); } } if(dfn[x]==low[x]) { int tmp=0; bl++; do{ tmp=st.top(); st.pop(); c[tmp]=bl,vis[tmp]=0; }while(tmp!=x); } } void dfs(int x) { if(marked[x]) return; marked[x]=1; for(auto y:g2[x]) { dfs(y); } } int main() { cin>>n>>m; memset(l,INF,sizeof(l)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { char c; cin>>c; if(c=='#') { s2[j].push_back(make_pair(i,cnt)); s[j].insert(make_pair(i,cnt++)); v.push_back(make_pair(i,j)); } } } for(int i=1;i<=m;i++) { scanf("%d",&a[i]); } for(int i=1;i<=m;i++) { if(!a[i]) continue; b[i]=s2[i][s2[i].size()-a[i]].second; sp.push_back(b[i]); } for(int i=0;i<cnt;i++) { int x=v[i].fi,y=v[i].se; auto it=s[y].upper_bound(make_pair(x,INF)); if(it!=s[y].end()) { e.push_back(make_pair(i,it->second)); g[i].push_back(it->second); } it=s[y-1].lower_bound(make_pair(x,0)); if(it!=s[y-1].end()) { e.push_back(make_pair(i,it->second)); g[i].push_back(it->second); } it=s[y+1].lower_bound(make_pair(x,0)); if(it!=s[y+1].end()) { e.push_back(make_pair(i,it->second)); g[i].push_back(it->second); } it=s[y].lower_bound(make_pair(x,0)); if(it--!=s[y].begin() && it->first+1==x) { e.push_back(make_pair(i,it->second)); g[i].push_back(it->second); } } for(int i=0;i<cnt;i++) { if(!dfn[i]) tarjan(i); } for(int i=0;i<e.size();i++) { int x=e[i].fi,y=e[i].se; x=c[x],y=c[y]; if(x!=y) { g2[x].push_back(y); } } for(int i=0;i<sp.size();i++) { int x=sp[i]; x=c[x]; for(auto y:g2[x]) { dfs(y); } } for(int i=1;i<=bl;i++) g2[i].clear(); for(int i=0;i<sp.size();i++) { int x=sp[i]; x=c[x]; if(marked[x]) continue; idx++; //重新编号 l[x]=min(l[x],idx); r[x]=max(r[x],idx); } for(int i=0;i<e.size();i++) { int x=e[i].fi,y=e[i].se; x=c[x],y=c[y]; if(x!=y) { g2[y].push_back(x); in[x]++; } } for(int i=1;i<=bl;i++) { if(!in[i]) { q.push(i); } } while(q.size()) { int x=q.front(); q.pop(); for(auto y:g2[x]) { l[y]=min(l[y],l[x]); r[y]=max(r[y],r[x]); if(--in[y] == 0) q.push(y); } if(!g2[x].size() && l[x] <= r[x]) { d[++siz]=make_pair(l[x],r[x]); // cout<<l[x]<<" "<<r[x]<<endl; } } sort(d+1,d+1+siz); int res(0),Max(1),p(1); while(Max<=idx) { int R(0); while(p<=siz&&d[p].fi<=Max) R=max(R,d[p].se),p++; res++,Max=R+1; } cout<<res; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530301.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示