[硫化铂]星际航道

星际航道

题目概述

在这里插入图片描述
在这里插入图片描述

题解

这道题要强制在线求出网格图的最小生成树,看起来很难搞的样子。
如果离线下来的话是可以线段树分治的,但在线就不好弄了。
我们想想在线我们会如何进行边的替换。
如果我们的 w w w是小于这条边原来的 w w w,那是不是这条边就变得更优了。
如果这条边原来就在我们的生成树,那就保持不变嘛,如果不在,我们就看它是否能替换原树上的边。
显然,这条边在树上插上去后会形成一个环,此时我们减去环上的最长边肯定是最优的,所以我们替换掉的肯定是两端点在树上路径中的最长边。
我们将它维护出来与 w w w比大小即可,这东西用 L C T LCT LCT很好维护。

但如果我们的 w w w如果是变大的不就做不了了吗。
没关系,我们这是网格图,我们可以考虑将它转对偶图。
容易发现,我们将网格图的一个生成树去掉后,剩下的边恰好可以在对偶图上形成一个生成树。
这点也非常容易证明,对偶图上的任意一个环必定对应着网格图中两个集合没有联通,反之亦然。
所以我们最后得到的其实是 网格图 中的 最小生成树 + 对偶图 中的 最大生成树
那么我们其实可以维护两棵树,当它变小时就在第一棵树上找到它是不是能替换边以及替换哪条边,当它变大时就在第二棵树上找边,然后再进行删边加边的操作。
这样,我们就做到 O ( log ⁡ n ) O\left(\log n\right) O(logn)维护单次操作了。

时间复杂度 O ( q log ⁡ n ) O\left(q\log n\right) O(qlogn)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast") 
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 100005
#define MAXM 500005
#define MAXQ 200005
#define pb push_back
#define mkpr make_pair
#define fir first 
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
const int INF=0x3f3f3f3f;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f; 
}
template<typename _T>
void print(_T x){if(x>9)print(x/10);putchar(x%10+'0');}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
struct edge{int u1,v1,u2,v2,w,col;}s[MAXM];
struct ming{int ch[2],fa,maxx,mxid,minn,mnid,val,id;bool rev;};
int typ,R,C,q,n1,n2,m,id[MAXN][2];
class LinkCutTree{
	private:
		ming tr[MAXM];int sta[MAXM],stak,nn;
		void pushup(int rt){
			tr[rt].maxx=(rt<=nn?-1:tr[rt].val),tr[rt].mxid=tr[rt].id;
			tr[rt].minn=(rt<=nn?INF:tr[rt].val),tr[rt].mnid=tr[rt].id;
			if(tr[rt].ch[0]&&tr[tr[rt].ch[0]].maxx>tr[rt].maxx)
				tr[rt].maxx=tr[tr[rt].ch[0]].maxx,tr[rt].mxid=tr[tr[rt].ch[0]].mxid;
			if(tr[rt].ch[0]&&tr[tr[rt].ch[0]].minn<tr[rt].minn)
				tr[rt].minn=tr[tr[rt].ch[0]].minn,tr[rt].mnid=tr[tr[rt].ch[0]].mnid;
			if(tr[rt].ch[1]&&tr[tr[rt].ch[1]].maxx>tr[rt].maxx)
				tr[rt].maxx=tr[tr[rt].ch[1]].maxx,tr[rt].mxid=tr[tr[rt].ch[1]].mxid;
			if(tr[rt].ch[1]&&tr[tr[rt].ch[1]].minn<tr[rt].minn)
				tr[rt].minn=tr[tr[rt].ch[1]].minn,tr[rt].mnid=tr[tr[rt].ch[1]].mnid;
		}
		void Reverse(int rt){swap(tr[rt].ch[0],tr[rt].ch[1]);tr[rt].rev^=1;}
		void pushdown(int rt){
			if(tr[rt].rev){
				if(tr[rt].ch[0])Reverse(tr[rt].ch[0]);
				if(tr[rt].ch[1])Reverse(tr[rt].ch[1]);
				tr[rt].rev=0;
			}
		}
		bool identify(int x){return tr[tr[x].fa].ch[1]==x;}
		bool isRoot(int x){return (tr[tr[x].fa].ch[0]^x)&&(tr[tr[x].fa].ch[1]^x);}
		void link(int x,int y,int d){tr[x].fa=y;tr[y].ch[d]=x;}
		void rotate(int x){
			int y=tr[x].fa,z=tr[y].fa,d1=identify(x),d2=identify(y);
			if(isRoot(y))tr[x].fa=z;else link(x,z,d2);int t=tr[x].ch[d1^1];
			link(t,y,d1);link(y,x,d1^1);pushup(y);pushup(x);
		}
		void splay(int x){
			int y=sta[stak=1]=x;while(!isRoot(y))sta[++stak]=y=tr[y].fa;
			while(stak)pushdown(sta[stak--]);
			for(y=tr[x].fa;!isRoot(x);rotate(x),y=tr[x].fa)
				if(!isRoot(y))rotate(identify(x)==identify(y)?y:x);
		}
		void access(int x){for(int y=0;x;x=tr[y=x].fa)splay(x),tr[x].ch[1]=y,pushup(x);}
		void makeRoot(int x){access(x);splay(x);Reverse(x);}
		int findRoot(int x){
			access(x);splay(x);pushdown(x);
			while(tr[x].ch[0])pushdown(x=tr[x].ch[0]);
			splay(x);return x; 
		}
	public:
		bool queryLink(int u,int v){makeRoot(u);return findRoot(v)==u;}
		int splitMax(int x,int y){makeRoot(x);access(y);splay(y);return tr[y].mxid;}
		int splitMin(int x,int y){makeRoot(x);access(y);splay(y);return tr[y].mnid;}
		void linkTree(int u,int v){makeRoot(u);if(findRoot(v)^u)tr[u].fa=v;}
		void cutTree(int u,int v){if(!queryLink(u,v))return ;tr[v].fa=tr[u].ch[0]=0;pushup(u);}
		void modify(int x,int w){tr[x].maxx=tr[x].minn=tr[x].val=w;}
		void init(int x){
			for(int i=1;i<=m;i++)tr[i+x].mxid=tr[i+x].mnid=tr[i+x].id=i;
			for(int i=1;i<=x;i++)tr[i].maxx=-1,tr[i].minn=INF;
			nn=x;
		}
}T1,T2;
int Id1(int x,int y){return (x-1)*C+y;}
int Id2(int x,int y){
	if(x<1||y<1||x>=R||y>=C)return (R-1)*(C-1)+1;
	return (x-1)*(C-1)+y;
} 
int fa[MAXN];
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u^v)fa[u]=v;}
inline void decode(char ch,int &x,int &y,int &w,LL lstans) {
	static int mask=0xfffff;
	w=(int)((w^lstans)&mask);
	if(ch=='-')x=(lstans+x-1)%R+1,y=(lstans+y-1)%(C-1)+1;
	if(ch=='|')x=(lstans+x-1)%(R-1)+1,y=(lstans+y-1)%C+1;
}
int main(){
	freopen("channel.in","r",stdin);
	freopen("channel.out","w",stdout);
	read(typ);read(R);read(C);read(q);
	n1=R*C;n2=(R-1)*(C-1)+1;LL lastans=0,summ=0;
	for(int i=1;i<=R;i++)
		for(int j=1;j<C;j++){
			m++;s[m].w=0;id[Id1(i,j)][0]=m;
			s[m].u1=Id1(i,j);s[m].v1=Id1(i,j+1);
			s[m].u2=Id2(i-1,j);s[m].v2=Id2(i,j);
		}
	for(int i=1;i<R;i++)
		for(int j=1;j<=C;j++){
			m++;s[m].w=0;id[Id1(i,j)][1]=m;
			s[m].u1=Id1(i,j);s[m].v1=Id1(i+1,j);
			s[m].u2=Id2(i,j-1);s[m].v2=Id2(i,j);
		}
	T1.init(n1);T2.init(n2);makeSet(n1);
	for(int i=1;i<=m;i++){
		if(findSet(s[i].u1)==findSet(s[i].v1))s[i].col=0;
		else s[i].col=1,unionSet(s[i].u1,s[i].v1);
		if(s[i].col)T1.linkTree(s[i].u1,i+n1),T1.linkTree(i+n1,s[i].v1);
		else T2.linkTree(s[i].u2,i+n2),T2.linkTree(i+n2,s[i].v2);
	}
	for(int i=1;i<=q;i++){
		char opt[5];int x,y,w;
		scanf("\n%s",opt);read(x);read(y);read(w);
		if(typ)decode(opt[0],x,y,w,lastans);
		int p=id[Id1(x,y)][opt[0]=='|'];
		if(s[p].w>w){
			if(s[p].col){
				summ-=s[p].w;summ+=w;
				T1.cutTree(s[p].u1,p+n1);
				T1.cutTree(s[p].v1,p+n1);
				T1.modify(p+n1,w);T2.modify(p+n2,w);s[p].w=w;
				T1.linkTree(s[p].u1,p+n1);
				T1.linkTree(s[p].v1,p+n1);
			}
			else{
				int t=T1.splitMax(s[p].u1,s[p].v1);
				T2.cutTree(s[p].u2,p+n2);
				T2.cutTree(s[p].v2,p+n2);
				T2.modify(p+n2,w);T1.modify(p+n1,w);
				if(s[t].w>w){
					summ-=s[t].w;summ+=w;
					T1.cutTree(s[t].u1,t+n1);
					T1.cutTree(s[t].v1,t+n1);
					T1.linkTree(s[p].u1,p+n1);
					T1.linkTree(s[p].v1,p+n1);	
					T2.linkTree(s[t].u2,t+n2);
					T2.linkTree(s[t].v2,t+n2);
					s[p].col=1;s[t].col=0;
				}
				else{
					T2.linkTree(s[p].u2,p+n2);
					T2.linkTree(s[p].v2,p+n2);
				}
				s[p].w=w;
			}
		}
		else{
			if(!s[p].col){
				T2.cutTree(s[p].u2,p+n2);
				T2.cutTree(s[p].v2,p+n2);
				T2.modify(p+n2,w);T1.modify(p+n1,w);s[p].w=w;
				T2.linkTree(s[p].u2,p+n2);
				T2.linkTree(s[p].v2,p+n2);
			}
			else{
				int t=T2.splitMin(s[p].u2,s[p].v2);
				T1.cutTree(s[p].u1,p+n1);
				T1.cutTree(s[p].v1,p+n1);
				T1.modify(p+n1,w);T2.modify(p+n2,w);
				if(s[t].w<w){
					summ-=s[p].w;summ+=s[t].w;
					T2.cutTree(s[t].u2,t+n2);
					T2.cutTree(s[t].v2,t+n2);
					T2.linkTree(s[p].u2,p+n2);
					T2.linkTree(s[p].v2,p+n2);	
					T1.linkTree(s[t].u1,t+n1);
					T1.linkTree(s[t].v1,t+n1);
					s[p].col=0;s[t].col=1;
				}
				else{
					summ-=s[p].w;summ+=w;
					T1.linkTree(s[p].u1,p+n1);
					T1.linkTree(s[p].v1,p+n1);
				}
				s[p].w=w;
			}
		}
		printf("%lld\n",lastans=summ);
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值