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

题目链接

题目链接

题意

网格上有若干黑色格子。有两种技能,分别可以把所有黑色格子向【左/右】或【上/下】的方向扩展一格,两种技能第 \(i\) 次分别要用 \(a_i,b_i\) 的代价(洛谷原题代价为 1)。用最小的代价使所有黑色格子八连通。\(n,m\leq 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;
}

posted @ 2021-10-08 18:42  破壁人五号  阅读(70)  评论(0编辑  收藏  举报