满分做法:
将正反两面连边,发现如果一个连通块的变数>=n条时,那么这个连通块一定能满足保证每个面值都能够被提供至少一次。如果它是一棵树,无论怎么调整,都会漏掉一个面值无法打出。
于是最后询问的就是一个线段中是否包含一个完整的限制线段(他的起点为一棵树中编号最小的,终点为最大的),这个可以用离线树状数组解决,把询问线段和限制线段按照右端点从小到大排序。
一次处理询问,把右端点小于此询问线段右端点的限制线段加到树状数组中(在左端点+1),当区间和为0时可以满足条件,不为0则不能打出。
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxm=1e5+7;
int n,k,q;
int f[maxm],bian[maxm],dian[maxm],maxx[maxm],minn[maxm];
int c[maxm];
bool ans[maxm];
int find(int x)
{
if(x!=f[x])
f[x]=find(f[x]);
return f[x];
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x)
{
for(;x<=n;x+=lowbit(x))
c[x]+=1;
}
int ask(int x)
{
int ans=0;
for(;x;x-=lowbit(x))
ans+=c[x];
return ans;
}
struct node
{
int l,r,id;
bool operator <(const node &s) const
{
return r<s.r;
}
}qwq[maxm],qaq[maxm];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
f[i]=i;
bian[i]=0;
dian[i]=1;
maxx[i]=i;
minn[i]=i;
}
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int fx=find(x);
int fy=find(y);
if(fx==fy)
{
bian[fx]++;
continue;
}
if(fx!=fy)
{
f[fx]=fy;
dian[fy]+=dian[fx];
bian[fy]+=bian[fx]+1;
dian[fx]=bian[fx]=0;
maxx[fy]=max(maxx[fy],maxx[fx]);
minn[fy]=min(minn[fy],minn[fx]);
}
}
int tot=0;
for(int i=1;i<=n;i++)
{
if(dian[i]==bian[i]+1)//一个点也是一棵树
{
qwq[++tot].l=minn[i];
qwq[tot].r=maxx[i];
}
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
qaq[i].l=x,qaq[i].r=y,qaq[i].id=i;
}
sort(qwq+1,qwq+tot+1);
sort(qaq+1,qaq+q+1);
int top=1;
for(int i=1;i<=q;i++)
{
while(top<=tot&&qwq[top].r<=qaq[i].r)
{
add(qwq[top].l);
top++;
}
if(ask(qaq[i].r)-ask(qaq[i].l-1))//有完整线段
ans[qaq[i].id]=1;
}
for(int i=1;i<=q;i++)
{
if(ans[i])
printf("No\n");
else
printf("Yes\n");
}
return 0;
}