zoukankan      html  css  js  c++  java
  • 游戏「AHOI / HNOI2018」

    题意

    (n)个房间,第(i)个房间与第(i+1)个房间之间有一扇门,有些门上了锁。已知所有上了锁的门的编号,以及每一把锁在哪个房间,有多组询问:能否从(a)房间到达(b)房间?

    思路

    考虑优化暴力。

    对于每一个节点,维护L[i],R[i],表示该节点能够到达最左右极点。

    对于任意一扇上了锁的门,如果钥匙在门的左边,那么这扇门右边的房间肯定都到不了左边的房间,而左边的房间有可能达到右边的房间。如果钥匙在门的右边则同理。

    也就是说,对于一扇上了锁的门,右边的每一个节点的[L,R]都不可能包含左边的节点的[L,R],而反过来是有可能的。

    可以考虑这样的一种拓展顺序:

    对于拓展序列中的每一个节点,位于它前面的任意节点都不可能包含它的区间。

    显然这样的拓展顺序是最优的,因为我们的更新始终不会被干扰。

    为了得到这样的拓展顺序,考虑采用拓扑排序。对于钥匙在门左侧的情况,从右边连向左边的节点,反之亦然。然后跑一遍拓扑排序即可得到我们需要的拓展顺序,最后暴力拓展即可。

    但是这样写还是会T,所以需要进一步优化。

    考虑这样一种情况:只有一个门上了锁,而其他所有门都没有上锁。

    对于这样的情况,我们的程序仍然有n的计算量,而事实上只需要1的计算量。

    可以对数据进行预处理,将不要钥匙就可以直接到达的点锁在一起,然后对缩完点的序列再进行拓扑排序,这样对于上述情况就可以做的高效处理了。


    代码

    无缩点环节,70分代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    namespace StandardIO {
    
    	template<typename T> inline void read (T &x) {
    		x=0;T f=1;char c=getchar();
    		for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
    		for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
    		x*=f;
    	}
    	template<typename T> inline void write (T x) {
    		if (x<0) putchar('-'),x=-x;
    		if (x>=10) write(x/10);
    		putchar((x%10)+'0');
    	}
    
    }
    
    using namespace StandardIO;
    
    namespace Solve {
    	
    	const int N=1000010;
    	
    	int n,m,p;
    	int key[N],l[N],r[N],indeg[N];
    	int cnt;
    	int head[N];
    	struct node {
    		int to,next;
    	} edge[N<<1];
    	int num;
    	int seq[N];
    	
    	inline void add (int a,int b) {
    		edge[++cnt].to=b,edge[cnt].next=head[a],head[a]=cnt,++indeg[b];
    	}
    	inline void topsort () {
    		queue<int> q;
    		for (register int i=1; i<=n; ++i) if (!indeg[i]) q.push(i);
    		while (!q.empty()) {
    			int cur=q.front();q.pop(),seq[++num]=cur;
    			l[cur]=r[cur]=cur;
    			for (register int i=head[cur]; i; i=edge[i].next) {
    				int to=edge[i].to;
    				--indeg[to];
    				if (!indeg[to]) q.push(to);
    			}
    		}
    	}
    
    	inline void Main () {
    		read(n),read(m),read(p);
    		for (register int i=1; i<=m; ++i) {
    			int x,y;
    			read(x),read(y);
    			key[x]=y;
    			if (y>x) add(x,x+1);
    			else add(x+1,x);
    		} 
    		topsort();
    		int counter=0;
    		for (register int i=1; i<=n; ++i) {
    			int now=seq[i];
    			while (1) {
    				int tl=l[now],tr=r[now];
    				while (tl>1&&(!key[tl-1]||(tl<=key[tl-1]&&key[tl-1]<=tr))) tl=l[tl-1];
    				while (tr<n&&(!key[tr]||(tl<=key[tr]&&key[tr]<=tr))) tr=r[tr+1];
    				if (tl==l[now]&&tr==r[now]) break;
    				l[now]=tl,r[now]=tr;
    			}
    		}
    		for (register int i=1; i<=p; ++i) {
    			int s,t;
    			read(s),read(t);
    			if (l[s]<=t&&t<=r[s]) puts("YES");
    			else puts("NO");
    		}
    	}
    
    }
    
    int main () {
    	Solve::Main();
    }
    
    
  • 相关阅读:
    学习进度条05
    构建之法阅读笔记03
    子数组和最大值算法
    学习进度条04
    学习进度条03
    定制小学四则运算
    单元测试示例
    构建之法阅读笔记02
    学习进度条02
    decimal扩展方法(转换为字符串,去掉末尾的0)
  • 原文地址:https://www.cnblogs.com/ilverene/p/11206029.html
Copyright © 2011-2022 走看看