• CF1534F2Falling Sand (Hard Version)


    正题

    题目链接:https://www.luogu.com.cn/problem/CF1534F2


    题目大意

    有一个\(n*m\)个网格,有的网格上有沙子,一个沙子被刷新后会下落到底并且刷新沿途中四周四连通的沙子,你可以选择一些沙子手动刷新。

    现在要求第\(i\)列至少有\(a_i\)个沙子下落,求至少手动刷新多少个沙子。

    \(1\leq n\times m\leq 4\times 10^5\)


    解题思路

    显然列要求的\(a_i\)就是要求最下面的那\(a_i\)个沙子被刷新。

    手动刷新的肯定都是每一列位置最高的沙子,然后刷新关系可以表示成一张有向图,而且是平面图,那么就说明一个沙子被刷新的条件是手动刷新了某个区间中位置最高的沙子。

    我们考虑求出这些区间,先建边,这样最多\(4nm\)条边,然后对于每个沙子要求区间的左端点我们从左往右从最高的沙子开始跑,然后每次走到的点标记删除,右区间就变成从右往左跑。

    得到这些区间后我们转换成若干个形如区间\([l,r]\)中必须要有一个\(1\)的限制,然后考虑\(dp\),用单调队列优化一下即可。

    时间复杂度:\(O(nm)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define p(x,y) ((((x)-1)*n)+(y))
    using namespace std;
    const int N=8e5+10;
    struct node{
        int to,next;
    }a[N*4];
    int n,m,tot,ls[N],l[N],r[N],lim[N],f[N];
    deque<int> q;vector<int> v[N];char s[N];
    void addl(int x,int y){
        a[++tot].to=y;
        a[tot].next=ls[x];
        ls[x]=tot;return;
    }
    void dfsl(int x){
        for(int i=ls[x];i;i=a[i].next){
            int y=a[i].to;
            if(l[y])continue;
            l[y]=l[x];dfsl(y);
        }
        return;
    }
    void dfsr(int x){
        for(int i=ls[x];i;i=a[i].next){
            int y=a[i].to;
            if(r[y])continue;
            r[y]=r[x];dfsr(y);
        }
        return;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)
                if(s[j]=='#')
                    v[j].push_back(n-i+1);
        }
        for(int i=1;i<=m;i++){
            if(v[i].empty())continue;
            for(int j=0;j<v[i].size()-1;j++){
                addl(p(i,v[i][j]),p(i,v[i][j+1]));
            	if(v[i][j+1]+1==v[i][j])addl(p(i,v[i][j+1]),p(i,v[i][j]));
            }
            for(int g=-1;g<2;g++){
                if(!g||!v[i+g].size())continue;
                int z=0,_=0;
                while(z<v[i+g].size()&&v[i+g][z]>v[i][0])z++;
                for(int h=v[i][0];h>=1;h--){
                    if(_<v[i].size()-1&&h==v[i][_+1])_++;
                    if(z<v[i+g].size()&&h==v[i+g][z])
                        addl(p(i,v[i][_]),p(i+g,v[i+g][z])),z++;
                }
            }
        }
        for(int i=1;i<=m;i++){
            if(v[i].empty())continue;
            int x=p(i,v[i][0]);
            if(!l[x])l[x]=i,dfsl(x);
        }
        for(int i=m;i>=1;i--){
            if(v[i].empty())continue;
            int x=p(i,v[i][0]);
            if(!r[x])r[x]=i,dfsr(x);
        }
        for(int i=1,x;i<=m;i++){
            scanf("%d",&x);
            for(int j=v[i].size()-1;j>=(int)v[i].size()-x;j--)
            {int y=p(i,v[i][j]);lim[r[y]]=max(lim[r[y]],l[y]);}
        }
        q.push_back(0);
        for(int i=1;i<=m;i++){
            f[i]=max(f[i],f[q.front()]+1);
            while(!q.empty()&&f[q.back()]>=f[i])q.pop_back();
            q.push_back(i);
            while(!q.empty()&&q.front()<lim[i])q.pop_front();
        }
        printf("%d\n",f[q.front()]);
        return 0;
    }
    
  • 相关阅读:
    如何使用ASP.NET2.0的“嵌入的资源”
    多核心计算机运算
    [翻译]注册协议(Register Protocol)
    [翻译]关于“异步可插协议”(About Asynchronous Pluggable Protocols(APPs))
    [ASP.NET]runat="server" causes the problem (< or &lt;)
    [翻译]将应用程序注册为URL协议(Registering an Application to a URL Protocol)
    【C# 转换编码格式】 唉,总是忘记的一个方法。
    在WinForm中借助WebBrowser控件使用 tinymce 总结
    sqlite 资料整理(一)
    sqlite 性能优化
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16573737.html
Copyright © 2020-2023  润新知