• 洛谷 P5443


    洛谷题目页面传送门

    题意见洛谷。

    (以下认为所有(log)同阶,用(log n)表示)

    首先在我认知范围内似乎稍微复杂一点的图上询问题都没有polylog做法。先来考虑暴力。

    先上一个sb都会的暴力:修改就直接改,查询就把所有符合条件的边连起来建出一张图跑DFS。(mathrm O(qm))

    再考虑一个稍微经过大脑的想法。假如问题是静态的话,我们可以将所有边和所有询问排个序,使得在之前有的边之后一定有,这样就可以two-pointers地递推建图了,并查集维护连通性即可。但是有修改怎么办呢?依然排序,只将永远不会被修改的边排序按上述方法加,有修改的边的话就把修改它的操作有序地存下来,每次查询就二分查找出每条有修改的边最后一次修改并符合条件就加入并查集,得出答案后撤销。时间复杂度(mathrm O!left(mlog n+q^2log n ight))

    事实上这两种暴力并不能视作并列。不难发现,暴力一单次操作复杂度仅与(m)相关,暴力二单次操作复杂度仅与(q)相关,但是暴力二还有一个单独的(m),而且这个(m)和暴力一里的(m)是做的同样的工作:往图里加边。如果把暴力一的DFS也看成并查集的话,完全可以这样理解:暴力二是一些连续的操作离线下来排序,可以将所有操作分成若干段抱团取暖,暴力二本质上是(1)段,而暴力一是(q)段。

    显然这两种分段方式都太极端了。不妨强行令每段大小相等,这样就有了分块的想法:每段(sz1)个操作,共(mathrm O!left(dfrac q{sz1} ight))段。这样总时间复杂度显然是(mathrm O!left(dfrac q{sz1}(mlog n+sz1^2log n) ight)=mathrm O!left(dfrac{qmlog n}{sz1}+qcdot sz1log n ight))。根据均值不等式,(sz1=sqrt m)时时间复杂度为最优为(mathrm O(qsqrt mlog n)),如果你常数跟我一样大就别想了老老实实优化吧,如果你常数跟fz一样小可以考虑卡过去。

    考虑优化。接下来分析复杂度的时候不考虑线性和线性乘以(log)的操作,因为它们对复杂度的影响实在是微乎其微。把剩下来的操作都拎出来整理一遍。

    1. 将没有修改的边排序:每块都要排一遍,(mathrm O!left(dfrac{qmlog n}{sz1} ight))
    2. 将没有修改的边加入可撤销并查集:每块都要加一遍,(mathrm O!left(dfrac{qmlog n}{sz1} ight))
    3. 将有修改的边加入可撤销并查集:(mathrm O!left(qcdot sz1log n ight))

    考虑操作(1)。注意到这一块和上一块都没有修改的边一定是排好序的了,我们只需要将上一块有修改这一块没有修改的边拎出来排序然后和之前那个归并一下即可。而第二类那种边单块只能有(mathrm O(sz1))条,总共就是(mathrm O(q))条。于是操作(1)(log)没了。然鹅复杂度没有变,因为下面有个复杂度一样的操作(悲)

    考虑操作(2)。显然这一部分是永远不会被撤销的,于是这一部分改到普通并查集模式,路径压缩+启发式合并可以变(log)(alpha)。至此总复杂度降到了(mathrm O!left(qsqrt{malpha(n)log n} ight))(还是很大)

    考虑操作(3)。可以并查集缩点,然后暴力连边跑DFS。这样复杂度显然是少了一个(log)了的。至此,令(sz1=sqrt{malpha(n)})即可拥有(mathrm O!left(qsqrt{malpha(n)} ight))的总复杂度。

    可把我给卡常卡死了。人傻常数大就是指我吧。邻接表需要用链式前向星(而且这样DFS还比并查集慢,我甚至怀疑我写假了,发现我假掉了欢迎在评论区D死小编哦)。

    (所以这个时间轴分块到底是啥套路,不太摸得清)

    下面贴代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define mp make_pair
    #define X first
    #define Y second
    #define pb push_back
    const int N=50000,M=100000,QU=100000;
    int n,m,qu;
    int a[M+1],b[M+1],w[M+1];
    struct query{int tp,x,y;}qry[QU+1];
    int ans[QU+1];
    vector<int> chged,unchged;
    int las_unchged[M+1];
    vector<int> chgid[M+1];
    vector<int> ask;
    bool cmp(int x,int y){return qry[x].y>qry[y].y;}
    bool cmp0(int x,int y){return w[x]>w[y];}
    struct ufset{
    	int fa[N+1],sz[N+1];
    	void init(){
    		for(int i=1;i<=n;i++)fa[i]=0,sz[i]=1;
    	}
    	int root(int x){return fa[x]?fa[x]=root(fa[x]):x;}
    	void mrg(int x,int y){
    		x=root(x),y=root(y);
    		if(x==y)return;
    		if(sz[x]>sz[y])swap(x,y);
    		fa[x]=y,sz[y]+=sz[x];
    	}
    	int _sz(int x){return sz[x];}
    }ufs;
    struct addedge{
    	int sz,head[N+1],nxt[2*M+1],val[2*M+1];
    	void init(){sz=0;}
    	void ae(int x,int y){
    		val[++sz]=y;nxt[sz]=head[x];head[x]=sz;
    	}
    }nei;
    vector<int> cc;
    bool vis[N+1];
    int dfs(int x){
    	vis[x]=true;
    	cc.pb(x);
    	int res=ufs._sz(x);
    	for(int i=nei.head[x];i;i=nei.nxt[i]){
    		int y=nei.val[i];
    		if(vis[y])continue;
    		res+=dfs(y);
    	}
    	return res;
    }
    int main(){
    //	freopen("C:\Users\chenx\OneDrive\桌面\06.in","r",stdin);
    	cin>>n>>m;
    	for(int i=1;i<=m;i++)scanf("%d%d%d",a+i,b+i,w+i);
    	cin>>qu;
    	for(int i=1;i<=qu;i++)scanf("%d%d%d",&qry[i].tp,&qry[i].x,&qry[i].y);
    	int sz1=sqrt((m+1)*4);
    	for(int i=1,ie;i<=qu;i=ie+1){
    		ie=min(qu,i+sz1-1);
    		memset(las_unchged,0,sizeof(las_unchged));
    		for(int j=0;j<unchged.size();j++)las_unchged[unchged[j]]=1;
    		chged.clear();ask.clear();
    		for(int j=1;j<=m;j++)chgid[j].clear();
    		for(int j=i;j<=ie;j++)
    			if(qry[j].tp==1){
    				if(chgid[qry[j].x].empty())chged.pb(qry[j].x);
    				chgid[qry[j].x].pb(j);
    			}
    			else ask.pb(j);
    		sort(ask.begin(),ask.end(),cmp);
    		vector<int> v,v0;
    		for(int j=1;j<=m;j++)if(chgid[j].empty()){
    			las_unchged[j]++;
    			if(las_unchged[j]==1)v.pb(j);
    		}
    		sort(v.begin(),v.end(),cmp0);
    		for(int j=0;j<unchged.size();j++)if(las_unchged[unchged[j]]==2)v0.pb(unchged[j]);
    		unchged.clear();
    		int now1=-1,now2=-1;
    		while(now1+1<v.size()||now2+1<v0.size()){
    			if(now1+1==v.size()||now2+1<v0.size()&&cmp0(v0[now2+1],v[now1+1]))unchged.pb(v0[++now2]);
    			else unchged.pb(v[++now1]);
    		}
    		ufs.init();
    		int now=-1;
    		for(int j=0;j<ask.size();j++){
    			while(now+1<unchged.size()&&qry[ask[j]].y<=w[unchged[now+1]])
    				now++,ufs.mrg(a[unchged[now]],b[unchged[now]]);
    			nei.init();
    			for(int k=0;k<chged.size();k++){
    				int fd=lower_bound(chgid[chged[k]].begin(),chgid[chged[k]].end(),ask[j])-chgid[chged[k]].begin()-1;
    				int w0=fd==-1?w[chged[k]]:qry[chgid[chged[k]][fd]].y;
    				if(qry[ask[j]].y>w0)continue; 
    				int ar=ufs.root(a[chged[k]]),br=ufs.root(b[chged[k]]);
    				nei.ae(ar,br),nei.ae(br,ar);
    				}
    			ans[ask[j]]=dfs(ufs.root(qry[ask[j]].x));
    			for(int k=0;k<chged.size();k++)nei.head[ufs.root(a[chged[k]])]=nei.head[ufs.root(b[chged[k]])]=0;
    			for(int k=0;k<cc.size();k++)vis[cc[k]]=false;
    			cc.clear();
    		}
    		for(int j=i;j<=ie;j++)if(qry[j].tp==1)w[qry[j].x]=qry[j].y;
    	}
    	for(int i=1;i<=qu;i++)if(qry[i].tp==2)printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    逆向工程工具介绍2-IDA
    汇编语言基础-1 基本语言元素
    Python常用标准库1-Turtle,Random,Time和Datetime
    Python的模块、包和库的概念
    Go语言的函数修饰符
    物理层2-物理层下面的传输媒体
    数据分析之两种用户分群方法(RFM和聚类)
    区间估计与假设检验公式
    源码分析过滤器与拦截器的区别
    Springboot拦截器使用及其底层源码剖析
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P5443.html
Copyright © 2020-2023  润新知