• 【题解】洛谷 P7274 草地 | 20211008 模拟赛【最小生成树 单调栈 LCT】


    题目链接

    题目链接

    题意

    网格上有若干黑色格子。有两种技能,分别可以把所有黑色格子向【左/右】或【上/下】的方向扩展一格,两种技能第 (i) 次分别要用 (a_i,b_i) 的代价(洛谷原题代价为 1)。用最小的代价使所有黑色格子八连通。(n,mleq 1000)

    题解

    多次技能之后,一个黑色格子会被放大到一个矩形,因此我们容易求出(单独考虑)两个黑色格子要连通需要的横向技能个数和纵向技能个数,接着可以连边求一个横向代价 max 加纵向代价 max 最小的生成树。暴力连边是 (n^4) 的,但是我们可以用单调栈连边,连出一个平面图,边数 (O(n^2))。接着依次加横向代价从小到大的边,求纵向代价的最小生成树,用 LCT 维护。

    #include<bits/stdc++.h>
    using namespace std;
    int getint(){ int x;scanf("%d",&x);return x; }
    mt19937 mt;
    const int N=1010;
    #define ll long long
    char s[N][N];
    int n,m,id[N][N],cnt,p[N*N],q[N*N],x[N][N];
    vector<pair<int,int>>e;
    ll v[N],w[N];
    
    void calc(){
    	memset(x,0,sizeof(x));
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			x[i][j]=s[i][j]=='1'?i:x[i-1][j];
    	for(int i=1;i<=n;i++){
    		vector<int>stk;
    		for(int j=1;j<=m;j++){
    			if(!x[i][j])continue;
    			if(s[i][j]=='1'){
    				for(int k:stk)
    					e.emplace_back(id[i][j],id[x[i][k]][k]);
    			}
    			while(stk.size()&&x[i][stk.back()]<=x[i][j])stk.pop_back();
    			stk.push_back(j);
    		}
    	}
    }
    
    namespace LCT{
    
    const int N=3e6+10;
    int eu[N],ev[N],ew[N];
    bool qaq[N];
    struct node{
        int ch[2],id,mx,fa;
        bool rev;
    };
    node t[N];
    inline void pushdown(int x){
        if(t[x].rev){
            swap(t[x].ch[0],t[x].ch[1]);
            t[t[x].ch[0]].rev^=1;
            t[t[x].ch[1]].rev^=1;
            t[x].rev=0;
        }
    }
    inline void pushup(int x){
        if(t[x].ch[0])t[t[x].ch[0]].fa=x;
        if(t[x].ch[1])t[t[x].ch[1]].fa=x;
        t[x].mx=x;
        if(ew[t[t[t[x].ch[0]].mx].id]>ew[t[t[x].mx].id])t[x].mx=t[t[x].ch[0]].mx;
        if(ew[t[t[t[x].ch[1]].mx].id]>ew[t[t[x].mx].id])t[x].mx=t[t[x].ch[1]].mx;
    }
    inline bool isroot(int x){ return t[t[x].fa].ch[0]!=x&&t[t[x].fa].ch[1]!=x; }
    inline bool which(int x){ return t[t[x].fa].ch[1]==x; }
    inline void rotate(int x){
        int f=t[x].fa,ff=t[f].fa;
        bool w=which(x);
        if(!isroot(f))t[ff].ch[which(f)]=x;
        t[f].ch[w]=t[x].ch[w^1];
        t[x].ch[w^1]=f;
        t[x].fa=ff;
        pushup(x);
        pushup(f);
        if(!isroot(x))pushup(ff);
    }
    int stk[N];
    inline void splay(int x){
        int y=x,top=0;
        while(!isroot(y))stk[++top]=y,y=t[y].fa;
        stk[++top]=y;
        for(int i=top;i;--i)pushdown(stk[i]);
    
        while(!isroot(x)){
            int f=t[x].fa;
            if(!isroot(f)){
                if(which(x)==which(f))rotate(f);
                else rotate(x);
            }
            rotate(x);
        }
    }
    
    inline void access(int x){
        for(int y=0;x;y=x,x=t[x].fa)
            splay(x),t[x].ch[1]=y,pushup(x);
    }
    inline int getroot(int x){
        access(x);
        splay(x);
        while(pushdown(x),t[x].ch[0])x=t[x].ch[0];
        return x;
    }
    inline void toroot(int x){
        access(x);
        splay(x);
        t[x].rev^=1;
    }
    inline void link(int x,int y){
        toroot(x);toroot(y);
        t[x].fa=y;
    }
    inline void cut(int x,int y){
        toroot(x);
        access(y);
        splay(y);
        t[y].ch[0]=t[x].fa=0;
        pushup(y);
    }
    
    int cnt;
    multiset<int>eee;
    void new_edge(int i){
        int u=eu[i],v=ev[i],w=ew[i];
        if(getroot(u)!=getroot(v)){
            ++cnt;
            t[cnt].id=i;
            t[cnt].mx=cnt;
            link(u,cnt);
            link(v,cnt);
            eee.insert(w);
            return;
        }
        toroot(u);
        access(v);
        splay(v);
        int c=t[v].mx;
        if(ew[t[c].id]<=w)return;
        eee.erase(eee.find(ew[t[c].id]));
        cut(c,eu[t[c].id]);
        cut(c,ev[t[c].id]);
        t[c].id=i;
        pushup(c);
        link(u,c);
        link(v,c);
        eee.insert(w);
    }
    
    }
    
    vector<int>ee[N]; 
    
    int main(){
    	n=getint(),m=getint();
    	for(int i=1;i<=m;i++)v[i+1]=v[i]+getint();
    	for(int i=1;i<=n;i++)w[i+1]=w[i]+getint();
    	for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(s[i][j]=='1')
    				id[i][j]=++cnt,p[cnt]=i,q[cnt]=j;
    
    	calc();
    
    	for(int i=1;i<=n;i++){
    		reverse(id[i]+1,id[i]+m+1);
    		reverse(s[i]+1,s[i]+m+1);
    	}
    	for(int i=1;i<=cnt;i++)
    		q[i]=m+1-q[i];
    	calc();
    
    	for(int i=1;i<=m;i++){
    		int y=0;
    		for(int j=1;j<=n;j++)
    			if(s[j][i]=='1'){
    				if(y)e.emplace_back(id[j][i],id[y][i]);
    				y=j;
    			}
    	}
    
    	LCT::cnt=cnt;
    	for(int i=0;i<e.size();i++){
    		LCT::eu[i+1]=e[i].first;
    		LCT::ev[i+1]=e[i].second;
    		LCT::ew[i+1]=abs(p[e[i].first]-p[e[i].second])+1;
    		ee[abs(q[e[i].first]-q[e[i].second])].push_back(i+1);
    	}
    	
    	long long ans=1e18;
    	for(int i=0;i<=m;i++){
    		for(int j:ee[i])
    			LCT::new_edge(j);
    		if(LCT::eee.size()+1<cnt)continue;
    		else ans=min(ans,v[i]+w[*prev(LCT::eee.end())-1]);
    	}
    	cout<<ans<<endl;
    }
    
    
    知识共享许可协议
    若文章内无特别说明,公开文章采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
  • 相关阅读:
    STM32学习笔记10(实时时钟RTC)
    STM32学习笔记9(SysTick滴答时钟)
    STM32学习笔记8(DMA控制器)
    STM32学习笔记7(增量式PID的实现)
    STM32学习笔记6(TIM通用模块生成PWM)
    STM32学习笔记5(TIM通用模块定时功能)
    STM32学习笔记4(TIM32位定时器的实现)
    特权同学笔记-榨干FPGA片上存储资源
    MWC四轴飞行器代码解读
    渣硕的硬件、嵌入式软开秋招
  • 原文地址:https://www.cnblogs.com/wallbreaker5th/p/15381306.html
Copyright © 2020-2023  润新知