[bzoj1594] [Usaco2008 Jan]猜数游戏
二分答案(二分没冲突的前Q-1个问题),用并查集判定(用法同bzoj 1576)
假设一个询问区间[l,r],最小干草堆数目是A,我们可以得出[l,r]上的干草堆数目都>=A。
二分出mid后,把1~mid个询问按照最小干草数量A降序排序。。如果这些询问的回答自相矛盾的话,就存在一个询问区间[l,r],它的每个位置都存在于之前的询问区间(指那些A值比当前A值大的区间)中。也就是说根据以前的询问我们得出[l,r]里面任何一堆干草数目都大于当前的A值,那就自相矛盾了。(注意如果多个区间A值相同的话就取交集。
就变成判断一段区间是否已被完全覆盖。。那就可以上并查集了。fa[i]表示i点所在的被覆盖的区间的左端点。
这样的话是O(nlogQ*alpha(n))的。。我们可以离散化一下。。因为只要知道当前区间中是否存在一个点没被覆盖过,所以只要从离散化后相邻两个值中多拿出一个点做代表就行了(前提是相邻的差值>1)。这样就只剩4*Q堆干草了。
卡了半天常。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=1000233; 7 struct zs{ 8 int l,r,id,mn; 9 }q[25003]; 10 int fa[100003],now[25003],map[50003],L[25003],R[25003],mp[100003]; 11 int j,k,n,m,l,r,mid,tot,premx,mnx,mny,mxx,mxy; 12 bool flag; 13 int ra;char rx; 14 inline int read(){ 15 rx=getchar();ra=0; 16 while(rx<'0'||rx>'9')rx=getchar(); 17 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 18 } 19 bool cmp(zs a,zs b){return a.mn>b.mn;} 20 inline bool can(int x){ 21 register int i,j,k;now[0]=0; 22 for(i=1;i<=m;i++)if(q[i].id<=x)L[++now[0]]=q[i].l,R[now[0]]=q[i].r,now[now[0]]=i; 23 memset(fa,0,(premx+1)<<2);premx=0; 24 for(i=1;i<=now[0];i=j+1){ 25 for(j=i;j<now[0]&&q[now[j+1]].mn==q[now[i]].mn;j++); 26 mnx=mny=tot;mxx=mxy=0; 27 for(k=i;k<=j;k++){ 28 if(L[k]<mnx)mnx=L[k];if(L[k]>mxx)mxx=L[k];if(R[k]<mny)mny=R[k];if(R[k]>mxy)mxy=R[k]; 29 } 30 for(flag=0,k=mny;k>=mxx;k=fa[k]-1) 31 if(!fa[k]){flag=1;break;} 32 if(!flag)return 0; 33 if(mxy>premx)premx=mxy; 34 for(k=mxy;k>=mnx;k--) 35 if(!fa[k])fa[k]=mnx; 36 else{k=fa[k];if(k>=mnx)fa[k]=mnx;} 37 } 38 return 1; 39 } 40 inline int get(int x){ 41 l=1;r=tot; 42 while(l<r){ 43 mid=(l+r+1)>>1; 44 if(mp[mid]>x)r=mid-1;else l=mid; 45 }return l; 46 } 47 int main(){ 48 register int i; 49 n=read();m=read();tot=m<<1; 50 for(i=1;i<=m;i++)q[i].l=map[i]=read(),q[i].r=map[i+m]=read(),q[i].mn=read(),q[i].id=i; 51 sort(map+1,map+1+tot);int tmp=0; 52 for(i=1;i<=tot;i++)if(map[i]!=map[i-1])map[++tmp]=map[i];tot=tmp;tmp=0; 53 for(i=1;i<=tot;i++)if(map[i]+1<map[i+1])mp[++tmp]=map[i],mp[++tmp]=map[i]+1;else mp[++tmp]=map[i]; 54 tot=tmp; 55 for(i=1;i<=m;i++)q[i].l=get(q[i].l),q[i].r=get(q[i].r); 56 sort(q+1,q+1+m,cmp); 57 l=1;r=m; 58 while(l<r){ 59 mid=(l+r+1)>>1; 60 if(can(mid))l=mid;else r=mid-1; 61 } 62 if(l==m)puts("0");else printf("%d\n",l+1); 63 return 0; 64 }
UPD:妈呀之前有个地方大和小打反了>_<..已更正
UPD2: 还需要特判 值不够用的情况..因为数字互不相同&上限是10^9。
一个询问(L,R,A)意味着这个区间里有R-L+1个>=A的数...数据(可能出题人也)没有考虑到这部分..
正常版本见51nod1336。有个做法是二分图匹配,不过复杂度感人。