[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堆干草了。

  卡了半天常。。

 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 }
View Code

 UPD:妈呀之前有个地方大和小打反了>_<..已更正

 UPD2: 还需要特判 值不够用的情况..因为数字互不相同&上限是10^9。

              一个询问(L,R,A)意味着这个区间里有R-L+1个>=A的数...数据(可能出题人也)没有考虑到这部分..

              正常版本见51nod1336。有个做法是二分图匹配,不过复杂度感人。

posted @ 2015-12-24 19:35  czllgzmzl  阅读(552)  评论(4编辑  收藏  举报