BZOJ5288 & 洛谷4436 & LOJ2508:[HNOI/AHOI2018]游戏——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=5288
https://www.luogu.org/problemnew/show/P4436
UPD:重温了一下发现代码只能用C++(NOI)才能过了,不知道原因,但也懒得重写了,反正是对的。
看洛谷题解里面清一色的暴力,连唯一正解也是用了奇技淫巧才过(当然本题解参考了那个题解)。
于是难受的我来简单说一下“正解”(如有错误请指出orz)。
先从暴力开始,对于每个点我们暴力找到其能够到达的最大的区间[l,r]。
一个优化:我们在加入新的点进入这个区间的时候,可以把该点的答案区间合并进区间内。
接下来是正解,首先是对于每个门i,如果钥匙在其左边则add(i+1,i),否则add(i,i+1),其中边的含义是从入点无法到达出点。
于是对这个图拓扑排序后可发现,点u的答案区间一定不包含比其排名大的点,则在暴力优化的帮助下,我们可以证明出需要我们暴力更新的次数只有O(n)次。
但是为什么又要按照那篇题解所说,“初始化序列倒着加会快”呢?
我把数据下载下来才发现一个坑。
对于不在拓扑图上的点,其更新后的答案区间可能会覆盖与它同级甚至比它排名小的点的答案区间,这样暴力优化就无用武之地,复杂度退化到O(n^2)。
所以我们必须缩点,才能保证算法的复杂度。
(话句话讲就是出题人都如此煞费苦心卡掉了复杂度不对的正解却让暴力AC了)
#include<cmath> #include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N=1e6+5; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int to,nxt; }e[N]; struct data{ int x,y; }d[N]; int n,m,p,head[N],cnt,id,to[N]; int key[N],l[N],r[N],deg[N]; inline void add(int u,int v){ e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;++deg[v]; } inline bool pan(int x,int y){ if(y<1||y>id)return 0; if(x<y)--y; return l[x]<=key[y]&&key[y]<=r[x]; } queue<int>q; void dfs(){ for(int i=1;i<=id;++i)if(!deg[i])q.push(i); while(!q.empty()){ int u=q.front();q.pop(); bool flag=1; while(flag){ flag=0; while(pan(u,l[u]-1))l[u]=l[l[u]-1],flag=1; while(pan(u,r[u]+1))r[u]=r[r[u]+1],flag=1; } for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(!(--deg[v]))q.push(v); } } } inline void init(){ key[n]=-1;id=1; for(int i=1;i<=n;++i){ to[i]=id; if(key[i])l[id]=r[id]=id++; }--id; for(int i=1;i<=m;++i){ int x=to[d[i].x],y=to[d[i].y]; key[x]=y; if(x<y)add(x,x+1); else add(x+1,x); } } int main(){ n=read(),m=read(),p=read(); for(int i=1;i<=m;++i){ d[i].x=read(),d[i].y=read(); key[d[i].x]=d[i].y; } init();dfs(); for(int i=1;i<=p;++i){ int x=to[read()],y=to[read()]; if(l[x]<=y&&y<=r[x])putchar('Y'),putchar('E'),putchar('S'),putchar('\n'); else putchar('N'),putchar('O'),putchar('\n'); } return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++