zoukankan      html  css  js  c++  java
  • 洛谷P3206 [HNOI2010]城市建设

    神仙题

    题目大意:

    有一张(n)个点(m)条边的无向联通图,每次修改一条边的边权,问每次修改之后这张图的最小生成树权值和

    话说是不是(cdq)题目都可以用什么数据结构莽过去啊……

    这道题目确实是(cdq)好题

    (cdq)分治一定要处理多维偏序问题吗?并不是,它的核心思想是一个用子问题计算对另一个子问题的贡献

    我们将这道题按照时间轴来分治,那么显然一个子问题对另一个子问题是存在贡献的

    我们将整张图上的边进行分类:

    在当前分治区间内涉及到修改的边称为动态边

    其他边称为静态边

    我们在分治过程中,动态边显然会随着区间的缩小而减少,那么我们怎么来处理静态边呢?

    仔细考虑,我们根本不可以发现,静态边可以分为三类:

    (1.)无用边:肯定不会作为最小生成树中的边

    对于这类边,我们可以直接扔掉

    (2.)必要边:必作为生成树中的边

    对于这种边,我们可以直接把边权计入答案,然后把边连接的两个点用并查集合并到一起

    (3.)其他边

    没什么办法了,直接传给子区间吧

    按照这种方法,每分治到一个更小的区间,其他区间的动态边就会变成静态边,当我们分治到(l==r)时,所有的边都会变为静态边,那么其他边将不存在,必要边贡献的答案就是答案了

    现在我们有两个问题:

    1:如何求出前两类边?

    求无用边,我们可以将所有动态边的边权设置为(inf),然后跑(Kruskal),最后没有被用到的静态边都是无用边

    求必要边,我们可以将所有动态边的边权设置为(-inf),还跑(Kruskal),最后被用到的静态边都是必要边

    正确性好像挺显然的(雾

    2:时间复杂度?

    假设当前区间内有(k)条动态边,那么除去必要边和无用边之后,最多留下(k+1)个点和(2k)条边

    由于(k)每次降低一倍,所以边数和点数都是都每次减少接近一倍,复杂度约为(O(nlog^2n))

    注意我们每次操作要先求必要边再求无用边复杂度才对!!!

    我们注意这道题,它的其他子问题对当前子问题的贡献并不是按照一般(cdq)的方法,而是在进入当前区间之前已经将贡献计算好了

    #include<bits/stdc++.h>
    using namespace std;
    namespace red{
    #define int long long
    #define mid ((l+r)>>1)
    #define lowbit(x) ((x)&(-x))
    	inline int read()
    	{
    		int x=0;char ch,f=1;
    		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    		if(ch=='-') f=0,ch=getchar();
    		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    		return f?x:-x;
    	}
    	const int N=1e5+10;
    	int n,m,ask;
    	struct edge
    	{
    		int x,y,val,opt;
    		inline bool operator < (const edge &t) const
    		{
    			return val<t.val;
    		}
    	}a[N];
    	struct moved
    	{
    		int x,y;
    	};
    	struct node
    	{
    		int num,val,ret;
    	}q[N];
    	struct dsu
    	{
    		int f[N],str[N];
    		struct blue {int x,y;};
    		stack<blue> st;
    		inline void init()
    		{
    			for(int i=1;i<=n;++i) f[i]=i,str[i]=1;
    		}
    		inline int find(int k)
    		{
    			return f[k]==k?k:find(f[k]);
    		}
    		inline void merge(int x,int y)
    		{
    			int tx=find(x),ty=find(y);
    			if(tx==ty) return;
    			if(str[tx]<str[ty]) swap(tx,ty);
    			str[tx]+=str[ty];
    			f[ty]=tx;
    			blue tmp;
    			tmp.x=tx,tmp.y=ty;
    			st.push(tmp);
    		}
    		inline void del()
    		{
    			blue now=st.top();
    			st.pop();
    			f[now.y]=now.y;
    			str[now.x]-=str[now.y];
    		}
    		inline void clear(int bot)//可撤销并查集
    		{
    			while(st.size()>bot) del();
    		}
    	}s1,s;//s全局
    	int ret[30];
    	//reduction无用边	
    	//contraction必须边
    	vector<edge> eg[30],tr;//存每一层静态边和临时静态边
    	vector<moved> rose;//存当前动态边
    	int bot[30];
    	bool book[N];
    	inline void push_down(int dep)
    	{
    		tr.clear();
    		for(int i=0;i<eg[dep].size();++i) tr.push_back(eg[dep][i]);
    		sort(tr.begin(),tr.end());
    		s1.clear(0);
    		ret[dep+1]=ret[dep];
    		for(int i=0;i<rose.size();++i) s1.merge(rose[i].x,rose[i].y);
    		rose.clear();
    		for(int tx,ty,i=0;i<tr.size();++i)//求必要边
    		{
    			tx=s1.find(tr[i].x),ty=s1.find(tr[i].y);
    			if(tx==ty) continue;
    			tr[i].opt=1;
    			s1.merge(tr[i].x,tr[i].y);
    			s.merge(tr[i].x,tr[i].y);
    			ret[dep+1]+=tr[i].val;
    		}
    		s1.clear(0);//撤销
    		for(int i=0;i<tr.size();++i)//求无用边
    		{
    			if(tr[i].opt) continue;
    			if(s.find(tr[i].x)==s.find(tr[i].y)||s1.find(tr[i].x)==s1.find(tr[i].y))
    			{
    				tr[i].opt=-1;
    				continue;
    			}
    			s1.merge(tr[i].x,tr[i].y);
    		}
    		s1.clear(0);//撤销
    		eg[dep+1].clear();
    		for(int tx,ty,i=0;i<tr.size();++i)
    		{
    			if(tr[i].opt) continue;
    			tx=s.find(tr[i].x),ty=s.find(tr[i].y);
    			if(tx==ty) continue;
    			edge tmp;
    			tmp.x=tx,tmp.y=ty,tmp.val=tr[i].val,tmp.opt=0;
    			eg[dep+1].push_back(tmp);
    		}
    		return;
    	}
    	inline void cdq(int l,int r,int dep)
    	{
    		bot[dep]=s.st.size();
    		if(l==r)
    		{
    			edge tmp;
    			tmp.x=s.find(a[q[r].num].x),tmp.y=s.find(a[q[r].num].y);//底层
    			tmp.val=q[r].val,tmp.opt=0;
    			a[q[r].num].val=q[r].val;//注意这里,直接更改
    			eg[dep].push_back(tmp);
    			push_down(dep);
    			q[r].ret=ret[dep+1];
    			s.clear(bot[dep-1]);
    			return;
    		}
    		for(int i=l;i<=mid;++i) book[q[i].num]=1;//递归左边时,右边的动态变静态
    		for(int i=mid+1;i<=r;++i)
    		{
    			if(book[q[i].num]) continue;
    			edge tmp;
    			tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
    			tmp.val=a[q[i].num].val,tmp.opt=0;
    			eg[dep].push_back(tmp);
    		}
    		for(int i=l;i<=mid;++i)//左边的动态存进去
    		{
    			moved tmp;
    			tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
    			rose.push_back(tmp);
    		}
    		
    		push_down(dep);//下放
    		
    		for(int i=mid+1;i<=r;++i)
    		{
    			if(book[q[i].num]) continue;
    			eg[dep].pop_back();
    		}
    		for(int i=l;i<=mid;++i) book[q[i].num]=0;//回溯
    		
    		cdq(l,mid,dep+1);
    		
    		for(int i=0;i<eg[dep].size();++i) eg[dep][i].opt=0;//去掉标记
    		
    		for(int i=mid+1;i<=r;++i) book[q[i].num]=1;
    		
    		for(int i=l;i<=mid;++i)//左边的动态变静态
    		{
    			if(book[q[i].num]) continue;
    			edge tmp;
    			tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
    			tmp.val=a[q[i].num].val,tmp.opt=0;
    			eg[dep].push_back(tmp);
    		}
    		for(int i=mid+1;i<=r;++i)//右边的动态放进去
    		{
    			book[q[i].num]=0;
    			moved tmp;
    			tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
    			rose.push_back(tmp);
    		}
    		push_down(dep);
    		cdq(mid+1,r,dep+1);
    		s.clear(bot[dep-1]);//撤销并查集
    	}
    	inline void main()
    	{
    		n=read(),m=read(),ask=read();
    		s1.init(),s.init();
    		for(int i=1;i<=m;++i) a[i].x=read(),a[i].y=read(),a[i].val=read();
    		for(int i=1;i<=ask;++i)
    		{
    			q[i].num=read(),q[i].val=read();
    			book[q[i].num]=1;
    		}
    		for(int i=1;i<=m;++i)
    		{
    			if(book[i]) continue;
    			eg[1].push_back(a[i]);
    		}
    		for(int i=1;i<=ask;++i) book[q[i].num]=0;
    		cdq(1,ask,1);
    		for(int i=1;i<=ask;++i) printf("%lld
    ",q[i].ret);
    	}
    }
    signed main()
    {
    	red::main();
    	return 0;
    }
    
  • 相关阅读:
    itk_interior
    itk_option define
    scrolled canvas tcl tk
    init.rc的disabled含义
    WIFI分析与移植
    ubuntu10.04命令挂载windows硬盘与U盘
    Android WIFI 分析
    Android WIFI 分析
    Android平台开发WIFI function portingWIFI功能移植
    Android平台开发WIFI function portingWIFI功能移植
  • 原文地址:https://www.cnblogs.com/knife-rose/p/12045917.html
Copyright © 2011-2022 走看看