【2021noip模拟赛day3】D. 双面扑克
【题意】
k张扑克正反面各有一个数字范围1-n,q次询问l-r能否用扑克表示出来
【分析】
没看到数据范围以前觉得是一个网络流的题
看了数据范围果断放弃
考虑把正反两个值之间连一个边,那么我们得到的图中,如果一个连通块为树,那么意味着这个树中最小-最大的区间不能被包含在l-r内
那么询问就变成了是否l-r没有包含任何一个不合法区间
我们将不合法区间按左端点降序排列,然后从右往左扫,计算出每个点最远向右扩展的位置nxt[i]
然后对于询问比较一下r和nxt[l]即可
【代码】
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5; int n,k; int fa[maxn],minn[maxn],maxx[maxn],eg[maxn]; int find(int x) { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } struct line { int l,r; }area[maxn]; bool cmp(line a,line b) { return a.l<b.l; } int rlim[maxn]; int main() { freopen("yy.in","r",stdin); freopen("yy.out","w",stdout); scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) fa[i]=minn[i]=maxx[i]=i; for(int i=1;i<=k;i++) { int x,y; scanf("%d%d",&x,&y); int fx=find(x),fy=find(y); if(fx!=fy) { minn[fx]=min(minn[fx],minn[fy]); maxx[fx]=max(maxx[fx],maxx[fy]); eg[fx]+=eg[fy]+1; fa[fy]=fx; } else eg[fx]++; } int cnt=0; for(int i=1;i<=n;i++) { int fi=find(i); if(i==fi && eg[i]<=maxx[i]-minn[i]) { area[++cnt].r=maxx[i]; area[cnt].l=minn[i]; } } sort(area+1,area+cnt+1,cmp); int rpos=n; for(int i=n;i>=1;i--) { while(cnt && area[cnt].l==i) { rpos=min(rpos,area[cnt].r-1); cnt--; } rlim[i]=rpos; } int q; scanf("%d",&q); int x,y; for(int i=1;i<=q;i++) { scanf("%d%d",&x,&y); if(y>rlim[x]) printf("No\n"); else printf("Yes\n"); } return 0; }