zoukankan      html  css  js  c++  java
  • 【2019.7.26 NOIP模拟赛 T3】化学反应(reaction)(线段树优化建图+Tarjan缩点+拓扑排序)

    题意转化

    考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数。

    于是,这个问题就被我们搬到了图上,成了一个图论题。

    优化建图

    考虑我们每次需要将一个区间向一个区间连边。

    则我们可以用线段树优化建图

    具体步骤就是,建两棵线段树,每次新建一个虚节点,然后把需要向外连边的区间在一棵线段树上向这个节点连边,并从这个节点在另一棵线段树上向应被连边的区间连边。

    求解答案

    建完图之后,考虑如何求答案。

    首先,我们(Tarjan)缩点,显然一个强连通分量内的所有节点可以相互到达。

    其次,我们可以发现,一个节点所能到达的区间必然是一段连续的区间。

    则我们对于每个强连通分量,维护其中节点所能到达的区间。

    然后拓扑排序,进行转移即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    #define M 50000
    #define LN 20
    #define X 1000000007
    #define Gmin(x,y) (x>(y)&&(x=(y)))
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    using namespace std;
    int n,m;struct Reaction {int l1,r1,l2,r2;}s[M+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    class GraphSolver//将问题搬到图上求解
    {
    	private:
    		#define P (N<<3)+M//点数
    		#define E (N<<3)+(M*LN<<2)//边数
    		template<int PS,int ES> class Graph//一张图
    		{
    			private:
    				int ee;
    			public:
    				int n,lnk[PS+5];struct edge {int to,nxt;}e[ES+5];I Graph() {n=1;}
    				I void add(CI x,CI y) {Gmax(n,x),Gmax(n,y),e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}//建边
    		};
    		template<int SZ,int PS,int ES> class SegmentTreeGraphBuilder//线段树优化建图
    		{
    			private:
    				#define L l,mid,S[rt][0]
    				#define R mid+1,r,S[rt][1]
    				int n,tot,rtI,rtO,S[SZ<<3][2];
    				I void BuildI(CI l,CI r,int& rt)//初始化入边线段树
    				{
    					if(l==r) return (void)(rt=l);rt=++tot;RI mid=l+r>>1;
    					BuildI(L),BuildI(R),G.add(rt,S[rt][0]),G.add(rt,S[rt][1]);//由父亲向儿子连边
    				}
    				I void BuildO(CI l,CI r,int& rt)//初始化出边线段树
    				{
    					if(l==r) return (void)(rt=l);rt=++tot;RI mid=l+r>>1;
    					BuildO(L),BuildO(R),G.add(S[rt][0],rt),G.add(S[rt][1],rt);//由儿子向父亲连边
    				}
    				I void LinkI(CI tx,CI tl,CI tr,CI l,CI r,CI rt)//在入边线段树上连边
    				{
    					if(tl<=l&&r<=tr) return G.add(tx,rt);RI mid=l+r>>1;
    					tl<=mid&&(LinkI(tx,tl,tr,L),0),tr>mid&&(LinkI(tx,tl,tr,R),0);
    				}
    				I void LinkO(CI tx,CI tl,CI tr,CI l,CI r,CI rt)//在出边线段树上连边
    				{
    					if(tl<=l&&r<=tr) return G.add(rt,tx);RI mid=l+r>>1;
    					tl<=mid&&(LinkO(tx,tl,tr,L),0),tr>mid&&(LinkO(tx,tl,tr,R),0);
    				}
    			public:
    				Graph<PS,ES> G;I void Build(CI _n) {tot=n=_n,BuildI(1,n,rtI),BuildO(1,n,rtO);}//初始化
    				I void Link(CI l1,CI r1,CI l2,CI r2) {++tot,LinkO(tot,l1,r1,1,n,rtO),LinkI(tot,l2,r2,1,n,rtI);}//区间向区间连边
    		};
    		template<int PS,int ES> class Tarjaner//Tarjan缩点建新图
    		{
    			private:
    				int n,d,T,dfn[PS+5],low[PS+5],vis[PS+5],S[PS+5],col[PS+5];
    				I void Tarjan(CI lim,Con Graph<PS,ES>& G,CI x,CI lst=0)//Tarjan
    				{
    					RI i;dfn[x]=low[x]=++d,vis[S[++T]=x]=1;
    					for(i=G.lnk[x];i;i=G.e[i].nxt)
    						dfn[G.e[i].to]?vis[G.e[i].to]&&Gmin(low[x],dfn[G.e[i].to])
    						:(Tarjan(lim,G,G.e[i].to,x),Gmin(low[x],low[G.e[i].to]));
    					if(low[x]^dfn[x]) return;col[x]=++n,Mn[n]=x,Mx[n]=x<=lim?x:0,vis[x]=0;
    					W(x^S[T]) col[S[T]]=n,Gmin(Mn[n],S[T]),S[T]<=lim&&Gmax(Mx[n],S[T]),vis[S[T--]]=0;--T;
    				}
    			public:
    				int Mn[PS+5],Mx[PS+5];Graph<PS,ES> nG;I int operator [] (CI x) {return col[x];}
    				I void ReBuild(CI lim,Con Graph<PS,ES>& G)//重构建新图
    				{
    					RI i,j;for(i=1;i<=G.n;++i) !dfn[i]&&(Tarjan(lim,G,i),0);
    					for(i=1;i<=G.n;++i) for(j=G.lnk[i];j;j=G.e[j].nxt)
    						col[i]^col[G.e[j].to]&&(nG.add(col[i],col[G.e[j].to]),0);//连边
    				}
    		};
    		template<int PS,int ES> class Topoer//拓扑排序求解答案
    		{
    			private:
    				int q[PS+5],deg[PS+5],fl[PS+5],fr[PS+5];
    			public:
    				I int operator [] (CI x) {return fr[x]-fl[x]+1;}
    				I void Work(int *Mn,int *Mx,Con Graph<PS,ES>& G)
    				{
    					RI i,j,k,H=1,T=0;
    					for(i=1;i<=G.n;++i) for(j=G.lnk[i];j;j=G.e[j].nxt) ++deg[G.e[j].to];
    					for(i=1;i<=G.n;++i) !deg[i]&&(q[++T]=i);W(H<=T)//拓扑排序
    						for(i=G.lnk[k=q[H++]];i;i=G.e[i].nxt) !--deg[G.e[i].to]&&(q[++T]=G.e[i].to);
    					for(i=T;i;--i)//倒过来转移
    					{
    						fl[q[i]]=Mn[q[i]],fr[q[i]]=Mx[q[i]];
    						for(j=G.lnk[q[i]];j;j=G.e[j].nxt) Gmin(fl[q[i]],fl[G.e[j].to]),Gmax(fr[q[i]],fr[G.e[j].to]);//维护每个强连通分量所能到达的区间
    					}
    				}
    		};
    		SegmentTreeGraphBuilder<N,P,E> S;Tarjaner<P,E> T;Topoer<P,E> O;
    	public:
    		I void Solve()
    		{
    			RI i,ans=0;for(S.Build(n),i=1;i<=m;++i) S.Link(s[i].l1,s[i].r1,s[i].l2,s[i].r2);//建边
    			for(T.ReBuild(n,S.G),O.Work(T.Mn,T.Mx,T.nG),i=1;i<=n;++i) ans=(1LL*i*O[T[i]]+ans)%X;//求解,计算答案
    			printf("%d",ans);//输出答案
    		}
    }S;
    int main()
    {
    	freopen("reaction.in","r",stdin),freopen("reaction.out","w",stdout);
    	RI i;for(F.read(n,m),i=1;i<=m;++i) F.read(s[i].l1,s[i].r1,s[i].l2,s[i].r2);
    	return S.Solve(),0;
    }
    
  • 相关阅读:
    Netty实现Unity登录验证(三)
    Netty实现Unity登录验证(二)
    Netty实现Unity登录验证(一)
    Unity RPC 链接
    摄像机跟随物体,修复物体遮挡
    Character Shader 含半透明及受击效果
    空Shader重新指认工具
    Box波浪运动的一种实现
    查找所有有多个 Texture 的 Matrial
    数据生成XML导入Excel
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Contest20190726T3.html
Copyright © 2011-2022 走看看