[APIO2007 backup]用贪心实现特殊图的费用流增广**

【题目】http://www.oi.bbjy.com/n38c10.aspx

【算法分析】

O(nk)的dp。测得70分….

然后我YY到一个费用流,发现复杂度还是O(nk),而且常数上必然比dp更慢。。。

于是翻看Sinya神牛的题解。。。Orz,原来是用贪心去做费用流。。。彻底膜拜。

http://sinyalee.com/blog/?p=427

拜一下先….

然后我YY到的费用流就和野牛的建图一样.

下面我就说一下我对那个贪心的理解.

首先我们想象我们是一只小蝌蚪,在流中寻找增广路….

(借一下野牛的图。。。)

然后可以YY到,我们找的增广路必然是从与S相连的一条未增广的边进去,然后向左或者向右若干次“上下波动”(当然也可以不上下动,直接进去T那里),然后跑到T点去。

如在a2->a3->a4这里波动。

我们如果增广路包含这一路径的话,就可以看成把a3给取消了,然后选择了a2和a4.
这样的话,连续的(不取,取,不取,取,不取….)就可以当成一个组。

注意最左边和最右边都必须为“不取”。

分成那个组以后,组的中间必然是不能做增广的,因为与S相连的边已经饱和~

所以就可以看成必须在两端搞进去。。。搞了以后,无论在左边开始搞还是右边开始搞,结果都是一样的,都是多了一个点,都是向两边迈进了一步。(画图YY一下即可)

然后就再把相邻的两组合并到一起。因为你和她已经弄成新的OXOXOXOXOXOXO….结构了

现在大体我们已经明了了,但是边沿情况怎么处理呢?

注意到,如果a1或者an已经选了,是不可能再取消的。那就别放进决策队列。

证明:

你YY一下费用流的增广过程,

会发现那里要么和S连的只属于一个点的边已经满流,要么和T连的只属于一个店的边已经满流,你是没有办法取消它了。。。

因为费用流是对的,所以这个是对的,因为这只是在模拟增广费用流。

然后综上一下,发现堆可以达到我们需要的每次找最短增广路径的目的。

增广增加的费用其实就是SUM[L[i],R[i]]-2*w[i]。

其中sum[i,j]表示a[i]到a[j]的和,w[i]表示这个区间已经选了的点的权值和。

根据这个做堆的关键字就可以了。

【其他】膜拜Sinya

【CODE】

#include #include #include #include using namespace std;
const int N=101111;
int n,K;
int a[N],inheap[N],fa[N],L[N],R[N];
int sum[N],ans;

inline int S(int i,int j){
       if (!i) return sum[j];
       return sum[j]-sum[i-1];
}

struct HeapType{
       struct pt{int pos;int w;}h[N];
       int tot;
      
       bool cmp(int i,int j){
            return (S(L[h[i].pos],R[h[i].pos])-h[i].w*2)
                  <(S(L[h[j].pos],R[h[j].pos])-h[j].w*2);
       }
      
       void swap(int i,int j){
            int tmp;
            tmp=h[i].pos; h[i].pos=h[j].pos; h[j].pos=tmp;
            tmp=h[i].w; h[i].w=h[j].w; h[j].w=tmp;
            inheap[h[i].pos]=i; inheap[h[j].pos]=j;
       }
      
       void up(int k){
            while (k>1 && cmp(k,k/2)){
                  swap(k/2,k);
                  k/=2;
            }
       }
      
       void down(int k){
            int p;
            while (k*2<=tot){
                  p=k*2;
                  if (p+1<=tot && cmp(p+1,p)) p++;
                  if (cmp(p,k)){
                    swap(p,k);
                    k=p;
                  }else break;
            }
       }
      
       void ins(int pos,int w){
            tot++;
            h[tot].pos=pos;
            h[tot].w=w;
            inheap[pos]=tot;
            up(tot);    
       }
      
       void del(int inh){
            if (!inh) return;
            int tmp=h[inh].pos;
            swap(tot,inh);
            inheap[tmp]=0;
            tot–;
            up(inh);
            down(inh);
       }
}heap;

void input(){
     cin >> n >> K;
     int x;
     for (int i=1;i<=n;i++){
         scanf("%d",&x);
         a[i]=x;
     }
     for (int i=1;i     n–;
     sum[0]=0;
     for (int i=1;i<=n+1;i++){
         L[i]=i; R[i]=i; fa[i]=i;
         sum[i]=sum[i-1]+a[i];
     }
     L[0]=0; R[0]=0; fa[0]=0;
     heap.tot=0;
     for (int i=1;i<=n;i++) heap.ins(i,0);
}

int gf(int k){
    if (fa[k]==k) return k;
    fa[k]=gf(fa[k]);
    return fa[k];
}

void work(){    
     int i,k,ans=0,Lc,Rc,p,nw;
     for (k=1;k<=K;k++){
         p=gf(heap.h[1].pos);
         ans+=S(L[p],R[p])-heap.h[1].w*2;
         nw=S(L[p],R[p])-heap.h[1].w;
         heap.del(1);
        
         Lc=gf(L[p]-1);
         nw+=heap.h[inheap[Lc]].w;
         L[p]=L[Lc];
         fa[Lc]=p;
         heap.del(inheap[Lc]);
        
         Rc=gf(R[p]+1);
         nw+=heap.h[inheap[Rc]].w;
         R[p]=R[Rc];
         fa[Rc]=p;
         heap.del(inheap[Rc]);
        
         if (L[p]>0 && R[p]<=n) heap.ins(p,nw);
     }
     cout << ans << endl;
}

int main(){
    freopen("backup.in","r",stdin);
    freopen("backup.out","w",stdout);
    input();
    work();
    return 0;
}

[APIO2007 mobiles]树形动态规划

【题目】http://www.oi.bbjy.com/n38c10.aspx

【算法分析】

送分题,树形DP之。

虽然我写的比较复杂,但是第一感觉就这个,没所谓了,反正能AC。

首先风铃只能分布于相邻的两层。

判断以后,设cd为靠上的一层。

然后

F[i][0]表示我下面的都是层数为cd的风铃。

F[i][1]表示我下面的都是层数为cd+1的风铃。

F[i][2]表示我下面的是由cd和cd+1的风铃混合而成,而且满足题目性质。即先是一段cd+1的,然后再是一段cd的。

然后转移一下,就可以了。

【其他】1A

【CODE】

#include #include const int N=101111;
const int INF=1000000000;
int n,cd;
int F[N][3],L[N],R[N],dep[N],v[N];

void input(){
     scanf("%d",&n);
     for (int i=1;i<=n;i++) scanf("%d%d",&L[i],&R[i]);
}

void Get_dep(){
     dep[1]=1;
     for (int i=1;i<=n;i++){
         if (L[i]!=-1) dep[L[i]]=dep[i]+1;
         if (R[i]!=-1) dep[R[i]]=dep[i]+1;
     }
}

bool can_same_dep(){
     memset(v,0,sizeof(v));
     int depnum=0;
     for (int i=1;i<=n;i++)
       if (L[i]==-1 || R[i]==-1){
         if (!v[dep[i]]) depnum++;
         v[dep[i]]++;
       }
     if (depnum>2) return false;
     if (depnum<=1) return true;
     for (int i=1;i<=n;i++)
       if (v[i])
         if (v[i+1]){
           cd=i;
           return true;
         }
         else return false;
}

inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x

void dp(){
     int i,j;
     for (i=1;i<=n;i++)
       for (j=0;j<3;j++)
         F[i][j]=INF;
     for (i=1;i<=n;i++)
       if (L[i]==-1 && R[i]==-1)
         if (dep[i]==cd) F[i][0]=0;
                    else F[i][1]=0;
     for (i=n;i>=1;i–)
       if (L[i]!=-1 || R[i]!=-1)
         if (L[i]==-1){
           if (dep[i]==cd){
             F[i][0]=F[R[i]][0];
             F[i][2]=min(F[i][2],F[R[i]][2]+1);
             F[i][2]=min(F[i][2],F[R[i]][1]+1);
           }
           else{
             F[i][1]=F[R[i]][1];
             F[i][2]=min(F[i][2],F[R[i]][2]);
             F[i][2]=min(F[i][2],F[R[i]][0]);
           }
         }
         else if (R[i]==-1){
           if (dep[i]==cd){
             F[i][0]=F[L[i]][0];
             F[i][2]=min(F[i][2],F[L[i]][1]);
             F[i][2]=min(F[i][2],F[L[i]][2]);
           }
           else{
             F[i][1]=F[L[i]][1];
             F[i][2]=min(F[i][2],F[L[i]][2]+1);
             F[i][2]=min(F[i][2],F[L[i]][0]+1);
           }
         }
         else{
           F[i][0]=min(F[i][0],F[L[i]][0]+F[R[i]][0]);
           F[i][1]=min(F[i][1],F[L[i]][1]+F[R[i]][1]);
           int &t=F[i][2];
           t=min(t,F[L[i]][1]+F[R[i]][0]);
           t=min(t,F[L[i]][1]+F[R[i]][2]);
           t=min(t,F[L[i]][2]+F[R[i]][1]+1);
           t=min(t,F[L[i]][2]+F[R[i]][0]);
           t=min(t,F[L[i]][0]+F[R[i]][1]+1);
           t=min(t,F[L[i]][0]+F[R[i]][2]+1);
         }
     int ans=INF;
     ans=min(ans,F[1][0]);
     ans=min(ans,F[1][1]);
     ans=min(ans,F[1][2]);
     if (ans==INF) printf("-1n");
              else printf("%dn",ans);
}

int main(){
    freopen("mobiles.in","r",stdin);
    freopen("mobiles.out","w",stdout);
    input();
    Get_dep();
    if (can_same_dep()) dp();
    else printf("-1n");
}

[HDU3356 Air Strike]枚举、面积分配

【题目大意】

给定两个圆心,两圆的面积和,以及N个点的坐标。给出最小有多少个点不能覆盖到。

【算法分析】

枚举“卡住”第一个圆的是哪个点,然后把剩下的面积分到另外一个圆去。

【其它】注意,第一个圆的半径可以为0….比赛的时候就因为这个没A

【CODE】

#include #include #include #include #include #define sqr(x) ((x)*(x))
using namespace std;
const double pi=3.141;
const double eps=1e-8;
const int N=1111;
int n;
double lx[N],ly[N],sum,s1,s2,r1,r2;

inline double dis(int i,int j){
    return sqr(lx[i]-lx[j])+sqr(ly[i]-ly[j]);
}   

int main(){
    int Tc=0,now,ans;
    for (;;){
        scanf("%d",&n);
        if (!n) break;
        Tc++; ans=0;
        scanf("%lf%lf%lf%lf%lf",&lx[n+1],&ly[n+1],&lx[n+2],&ly[n+2],&sum);
        for (int i=1;i<=n;i++) scanf("%lf%lf",&lx[i],&ly[i]);
        for (int i=1;i<=n+1;i++){
            r1=dis(i,n+1);
            s1=r1*pi;
            if (s1>sum+eps) continue;
            s2=sum-s1+eps;
            r2=s2/pi;
            now=0;
            for (int j=1;j<=n;j++)
              if (dis(j,n+1)<=r1+eps || dis(j,n+2)<=r2+eps) now++;
            ans=max(now,ans);
        }   
        cout << Tc << ". " << n-ans << endl;
    }   
}   

[APIO2009 convention]平衡树、贪心、2进制步长分解、离散化、字典序、单调性**

【题目】http://wenku.baidu.com/view/079ae6f9aef8941ea76e0599.html

【算法分析】非原创。。。。

首先不考虑字典序,那么就是按r排序,然后贪心取就可以了。

现在考虑字典序。

我们考虑到搞字典序有一个通法,那就是尝试加入某一个点,然后判断结果是否仍可以达到最优,如果是,那么就加进去。否则不加。然后就可以得到一个保证字典序的解了。。。

我们先对data按r排一下序,然后注意到如果一个区间包含另一个区间,那么大区间对判断能否构成最优解必然是没用的。删掉。。。

然后我们就会发现,不只R递增,连L也递增了!

然后设MIS(I,J)表示经过处理以后,第I个区间到第J个区间都可以选用,最多能连成多少个区间。

我们加入一条线段的话,那么就只需要判断是否满足

MIS(pos(preR),pos(L))+MIS(pos(R),pos(nextL))+1==MIS(pos(preR),pos(nextL))就行了。

现在来搞MIS

令right(i)^k=right(right(right(i)))…..(right()出现k次)

那么MIS(i,j)=max(k)   条件:right(i)^k<=j

如果是这样的话,就好搞多了。

我们就像rmq的ST算法一样把right(i)^(2^p)处理出来。

然后从i每次以2^step步长向后走,看什么时候不能走就行了。

因为存在一个性质:

2^0、2^1、2^2、2^3……..2^n

它们能通过每个只用一次的加法组成[1..2^(n+1)-1]这个区间里的任意数。

证明的话很容易,直接把+2^k相当于把二进制的某位数变成1,因为2进制与10进制一一对应,所以必然可以组合成。

纵观所以操作,发现平衡树可以解决。。。于是果断写了SBT。。。

【其它】代码写庛了。。。长达250行

【CODE】

#include #include #include #include using namespace std;
const int N=201111;
const int INF=1000000000;
struct Type{int l,r;}d[N],td[N];
int n,xt,tdn,ans,TOT;
int xl[N+N],lg[N],ansl[N];
int right[N][20];

struct SBTType{
    int tot,root;
    struct PointType{int l,r,s,zz;}tr[N];
   
    void update(int &p){
        if (!p) return;
        tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
    }   
   
    void left(int &p){
        int k=tr[p].r;
        tr[p].r=tr[k].l;
        tr[k].l=p;
        update(p);
        update(k);
        p=k;
    }   
   
    void right(int &p){
        int k=tr[p].l;
        tr[p].l=tr[k].r;
        tr[k].r=p;
        update(p);
        update(k);
        p=k;
    }
       
    void repair(int &p){
        if (!p) return;
        bool flag=false;
        if (tr[tr[tr[p].l].l].s>tr[tr[p].r].s){
          right(p);
          flag=true;
        }   
        if (tr[tr[tr[p].l].r].s>tr[tr[p].r].s){
            left(tr[p].l);
            right(p);
            flag=true;
        }   
        if (tr[tr[tr[p].r].r].s>tr[tr[p].l].s){
            left(p);
            flag=true;
        }   
        if (tr[tr[tr[p].r].l].s>tr[tr[p].l].s){
            right(tr[p].r);
            left(p);
            flag=true;
        }   
        if (flag){
            repair(tr[p].l);
            repair(tr[p].r);
            repair(p);
        }   
    }   
   
    void ins(int &p,int i){
        if (!p){
            p=++tot;
            tr[p].l=0; tr[p].r=0; tr[p].zz=i; update(p);
        }
        else{
            tr[p].s++;
            if (d[tr[p].zz].r                                 else ins(tr[p].l,i);
            repair(p);
        }   
    }   
   
    bool cross(int &p,int l,int r){
        if (!p) return false;
        if (r        if (l>d[tr[p].zz].r) return cross(tr[p].r,l,r);
        return true;
    }
   
    int FindR(int &p,int limit){
        if (!p) return 0;
        if (d[tr[p].zz].r<=limit) return max(d[tr[p].zz].r,FindR(tr[p].r,limit));
        return FindR(tr[p].l,limit);
    }   
   
    int FindL(int &p,int limit){
        if (!p) return INF;
        if (d[tr[p].zz].l>=limit) return min(d[tr[p].zz].l,FindL(tr[p].l,limit));
        return FindL(tr[p].r,limit);
    }   
}sbt;   

void input(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d%d",&d[i].l,&d[i].r);
    lg[1]=0;
    for (int i=2;i<=n;i++)
      lg[i]=lg[i-1]+(i==1<}   

int binary(int x){
    int l=1,r=xt,mid;
    while (l        mid=(l+r)/2;
        if (x<=xl[mid]) r=mid;
                   else l=mid+1;
    }   
    return l;
}   

void LiSan(){
    xt=0;
    for (int i=1;i<=n;i++){
        xl[++xt]=d[i].l;
        xl[++xt]=d[i].r;
    }
    sort(xl+1,xl+xt+1);
    int tot=xt; xt=0;
    for (int i=1;i<=tot;i++)
      if (!xt || xl[xt]!=xl[i])
        xl[++xt]=xl[i];
    for (int i=1;i<=n;i++){
        d[i].l=binary(d[i].l);
        d[i].r=binary(d[i].r);
    }   
}   

bool cmp(Type x,Type y){
    if (x.r    if (x.r>y.r) return false;
    if (x.l>y.l) return true;
    return false;
}   

void init(){
    for (int i=1;i<=n;i++) td[i]=d[i];
    sort(td+1,td+1+n,cmp);
    tdn=0;
    for (int i=1;i<=n;i++)
      if (!tdn || td[i].l>td[tdn].l)
        td[++tdn]=td[i];
}

void getans(){
    ans=1; int R=td[1].r;
    for (int i=2;i<=tdn;i++)
      if (td[i].l>R){
          ans++;
          R=td[i].r;
      }   
}   

void make_right(){
    for (int k=0;1<    for (int i=1;i<=tdn;i++){
        int l=i+1,r=tdn,mid;
        while (l            mid=(l+r)/2;
            if (td[i].r                              else l=mid+1;
        }   
        if (l>tdn || td[i].r>=td[l].l) right[i][0]=tdn+1;
                                  else right[i][0]=l;
    }   
    for (int k=1;1<      for (int i=1;i<=n;i++)
        right[i][k]=right[right[i][k-1]][k-1];
}  

int MIS(int l,int r){
    if (r    int res=1;
    for (int cur=l,step=lg[r-l+1];step>=0;step–)
      if (right[cur][step]<=r){
          res+=1<          cur=right[cur][step];
      }   
    return res;
}  

int Findmax(int x){
    int l=1,r=tdn,mid;
    while (l        mid=(l+r)/2;
        if (td[mid].l>x) r=mid;
                    else l=mid+1;
    }   
    if (td[l].l>x) return l;
    return INF;
}

int Findmin(int x){
    int l=1,r=tdn,mid;
    while (l+1        mid=(l+r)/2;
        if (td[mid].r                    else r=mid-1;
    }   
    if (td[r].r    if (td[l].r    return -INF;
}   

bool Can(int i){
    if (sbt.cross(sbt.root,d[i].l,d[i].r)) return false;
    int L,R,l,r,tmp1=0,tmp2=0;
    L=sbt.FindR(sbt.root,d[i].l-1);
    R=sbt.FindL(sbt.root,d[i].r+1);
    if (!L) L=1;       else L=Findmax(L);
    if (R==INF) R=tdn; else R=Findmin(R);
    l=Findmin(d[i].l);
    r=Findmax(d[i].r);
    tmp1=MIS(L,l);
    tmp2=MIS(r,R);
    if (tmp1+tmp2+1!=MIS(L,R)) return false;
    return true;
}   

void work(){
    for (int i=1;i<=n;i++)
      if (Can(i)){
        sbt.ins(sbt.root,i);
        ansl[++TOT]=i;
      }   
    printf("%dn",ans);
    for (int i=1;i<=ans;i++){
        printf("%d",ansl[i]);
        if (i!=ans) printf(" ");
    }   
    printf("n");
}   

int main(){
    freopen("convention.in","r",stdin);
    freopen("convention.out","w",stdout);
    input();
    LiSan();
    init();
    getans();
    make_right();
    work();
}   

[SPOJ685 SEQPAR]数学归纳法证明、平衡树、动态规划、标记上传

【题库地址】https://www.spoj.pl/

进去找685就好,直接发地址的好像没有登录是看不了的。

【题目大意】

给定一个序列Ai,让你把他分成K组,使得每一组的和不超过M,问M最小能是多少?

1<=N<=15000

-30000<=Ai<=30000

【算法分析】

可以用数学归纳法证明:假设已经确定了M,然后设能构成合法分组的最小组数为min,能构成合法分组的最大组数为max,那么对于x∈[min,max],则x必然存在一个合法分组方式。(即解的连续性)

然后剩下的就是动态规划。

Fmin[i]为A1..Ai这个序列,最少能划分为多少组,使之成为一个合法序列。

然后Fmin[i]=min{Fmin[j]+1} (s[i]-s[j]<=M)

同理Fmax也一样求。

然后转移那个地方用一个平衡树维护一下就好,平衡树以s[i]为key值,然后上面加一个类似线段树的标记,然后随时更新就好。

【其它】1A。

这题一点思路都没有,只能找GXX大神要解题报告了

Orz GXX 大神。。。

【CODE】

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
const int N=15111;
const int INF=1000000000;
int n,part;
int Fmax[N],Fmin[N],a[N],s[N],nowi;
struct SBTtype{
       struct point{int l,r,zz,Min,Max,s;}tr[N];
       int tot,root;
      
       inline void update(int &p){
              if (!p) return;
              tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
              tr[p].Min=Fmin[tr[p].zz];
              if (tr[p].l && tr[tr[p].l].Min<tr[p].min) tr[p].min=”tr[tr[p].l].Min;<br”>              if (tr[p].r && tr[tr[p].r].Min<tr[p].min) tr[p].min=”tr[tr[p].r].Min;<br”>              tr[p].Max=Fmax[tr[p].zz];
              if (tr[p].l && tr[tr[p].l].Max>tr[p].Max) tr[p].Max=tr[tr[p].l].Max;
              if (tr[p].r && tr[tr[p].r].Max>tr[p].Max) tr[p].Max=tr[tr[p].r].Max;
       }
      
       void left(int &p){
            int k=tr[p].r;
            tr[p].r=tr[k].l;
            tr[k].l=p;
            update(p);
            update(k);
            p=k;
       }
      
       void right(int &p){
            int k=tr[p].l;
            tr[p].l=tr[k].r;
            tr[k].r=p;
            update(p);
            update(k);
            p=k;
       }
      
       void repair(int &p){
            if (!p) return;
            bool flag=false;
            if (tr[tr[tr[p].l].l].s>tr[tr[p].r].s){
              right(p);
              flag=true;
            }
            if (tr[tr[tr[p].l].r].s>tr[tr[p].r].s){
              left(tr[p].l);
              right(p);
              flag=true;
            }
            if (tr[tr[tr[p].r].r].s>tr[tr[p].l].s){
              left(p);
              flag=true;
            }
            if (tr[tr[tr[p].r].l].s>tr[tr[p].l].s){
              right(tr[p].r);
              left(p);
              flag=true;
            }
            if (flag){
              repair(tr[p].l);
              repair(tr[p].r);
              repair(p);
            }
       }
      
       void ins(int &p,int i){
            if (!p){
              p=++tot;
              tr[p].l=0; tr[p].r=0; tr[p].zz=i;
              update(p);
            }
            else{
              tr[p].s++;
              if (s[i]<s[tr[p].zz]) ins(tr[p].l,i);<br=””>                               else ins(tr[p].r,i);
              update(p);
              repair(p);
            }
       }
      
       int Findmin(int &p,int key){
           if (!p) return INF;
           if (s[tr[p].zz]<key) return=”” findmin(tr[p].r,key);<br=””>           int re=Findmin(tr[p].l,key);
           re=min(re,Fmin[tr[p].zz]);
           if (tr[p].r) re=min(re,tr[tr[p].r].Min);
           return re;
       }
      
       int Findmax(int &p,int key){
           if (!p) return -INF;
           if (s[tr[p].zz]<key) return=”” findmax(tr[p].r,key);<br=””>           int re=Findmax(tr[p].l,key);
           re=max(re,Fmax[tr[p].zz]);
           if (tr[p].r) re=max(re,tr[tr[p].r].Max);
           return re;
       }
}sbt;</key)></key)></s[tr[p].zz])></tr[p].min)></tr[p].min)></iostream>
</cstring>
</cstdlib>
</cstdio>

void init(){
     scanf(“%d%d”,&n,&part);
     for (int i=1;i<=n;i++) scanf(“%d”,&a[i]);
     for (int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
}

bool can(int M){
     sbt.root=0; sbt.tot=0;
     sbt.ins(sbt.root,0);
     for (nowi=1;nowi<=n;nowi++){
         Fmin[nowi]=sbt.Findmin(sbt.root,s[nowi]-M)+1;
         Fmax[nowi]=sbt.Findmax(sbt.root,s[nowi]-M)+1;
         sbt.ins(sbt.root,nowi);
     }
     if (Fmin[n]<=part && part<=Fmax[n]) return true;
     return false;
}

void work(){
     int l=-INF,r=INF,mid;
     while (l<r){
           mid=(l+r)>>1;
           if (can(mid)) r=mid;
                    else l=mid+1;
     }
     printf(“%dn”,l);
}</r){

int main(){
//    freopen(“input.txt”,”r”,stdin);
//    freopen(“output.txt”,”w”,stdout);
    int Tc;
    cin >> Tc;
    for (int i=0;i<tc;i++){
        init();
        work();
    }
    return 0;
}</tc;i++){