3.1

$JOI2022$

$T1$

二分+贪心显然,由于是$subtask$然后竟然连$long\ long$也爆掉了,所以每个$subtask$都错了一个点...$100pts->9pts$,枯了

#include<bits/stdc++.h>
#define int long long
#define MAXN 300005
#define INF 1e18
using namespace std;
int A[MAXN],B[MAXN],N,M;
bool check(int Now)
{
     int ls=0;
     for(int i=1;i<=N;i++)
     {
          if(A[i]>B[i])
          {
              if(Now/A[i]+(Now%A[i]==0?0:1)>M) continue;
              ls+=(M-(Now/A[i]+(Now%A[i]==0?0:1)));
          }
          else
          {
              ls+=M;
          }
     }
     for(int i=1;i<=N;i++)
     {
          if(A[i]<=B[i])
          {
              ls-=Now/B[i]+((Now%B[i]==0?0:1));
          }
          else if(Now/A[i]+(Now%A[i]==0?0:1)>M)
          {
                ls-=(Now-M*A[i])/B[i]+((Now-M*A[i])%B[i]==0?0:1);
          }
          if(ls<0) return false;
     }
     return ls>=0;
}
signed main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%lld%lld",&N,&M);
    for(int i=1;i<=N;i++)
    {
        scanf("%lld",&A[i]);
    }
    for(int i=1;i<=N;i++)
    {
        scanf("%lld",&B[i]);
    }
//    check(41397427274960);
    int l=0,r=INF,mid,ans;
    while(l<=r)
    {
          mid=(l+r)>>1;
          if(check(mid))
          {
               ans=mid;
               l=mid+1;
          }
          else
          {
               r=mid-1;
          }
    }
    cout<<ans<<endl;
}
/*
3 3
19 4 5
2 6 2

2 1
9 7
2 6

41397427274960
27823531422776
4 25
1 2 3 4
1 2 3 4
*/

 

$T2$

预处理+二分

#include<bits/stdc++.h>
#define int long long
#define MAXN 200005
using namespace std;
int Fin[MAXN],res[MAXN],Num,n,q,y;
void sol(int id,int x)
{
     int now=1;
     while(x%2==0)
     {
           now*=2;
           x/=2;
     }
     Fin[id]=x;
     res[id]=now;
}
void Mid_search(int id)
{
     int now=lower_bound(res+1,res+1+n,id)-res;
     printf("%lld\n",Fin[now]);
}
signed main()
{
//  freopen("b.in","r",stdin);
//  freopen("b.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&Num);
        sol(i,Num);
        res[i]+=res[i-1];
    }
    scanf("%lld",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%lld",&y);
        Mid_search(y);
    }
}

 

$T3$

考虑维护两次倍增数组

考虑一个点能到达的区间是连续的一段,如果到了一个点,那么中间的点必然也可以到达

考虑一条线路,那么这个起点可以到所有在起点和终点之间的点

那么只需要维护每个点向左和向右能到达的最远点就好了

 

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,k,x,y,m,lg[maxn],q[maxn],id[maxn],lp[maxn],rp[maxn];
int l[18][maxn],ls[18][18][maxn],r[18][maxn],rs[18][18][maxn];
int getl(int t,int l,int r) {
    int k=lg[r-l+1];
    //传参,跳的步数,左端点,右端点
    //至于为什么维护一个区间,大概理解了,也就是说
    //你这几步能到的左右端点之间都能到达,然后一点一点扩大范围罢了 
    return min(ls[t][k][l],ls[t][k][r-(1<<k)+1]);
}
int getr(int t,int l,int r) {
    int k=lg[r-l+1];
    return max(rs[t][k][l],rs[t][k][r-(1<<k)+1]);
}
void prep(){
    lg[0]=-1;
    for(int i=1; i<=n; i++)
        lg[i]=lg[i>>1]+1,lp[i]=rp[i]=i;
}
int main() {
    scanf("%d%d%d",&n,&k,&m);
    prep();
    while(m--){
        scanf("%d%d",&x,&y);
        if(x>y)lp[x]=min(lp[x],y);
        else rp[x]=max(rp[x],y);
        //预处理每个点到左右最远的点 
    }
    for(int i=n,l=1,r=0; i>=1; i--) {
        //单调队列更新每个点的值 
        while(l<=r&&q[r]>lp[i])r--;
        while(l<=r&&id[l]>=i+k)l++;
        q[++r]=lp[i]; 
        id[r]=i; 
        lp[i]=min(q[l],i);
    }
    for(int i=1,l=1,r=0; i<=n; i++) {
        //单调队列更新每个点的值 
        while(l<=r&&q[r]<rp[i])r--;
        while(l<=r&&id[l]<=i-k)l++;
        q[++r]=rp[i]; 
        id[r]=i; 
        rp[i]=max(q[l],i);
    }
    for(int t=0; t<18; t++) {
        //三维
        //一维维护区间,一维维护步数
        for(int i=1; i<=n; i++) {
            ls[t][0][i]=l[t][i]=t?getl(t-1,l[t-1][i],r[t-1][i]):lp[i];
            rs[t][0][i]=r[t][i]=t?getr(t-1,l[t-1][i],r[t-1][i]):rp[i];
            //目前就是长度是1的区间,跳 
        }
        for(int k=1; k<18; k++)
            for(int i=1; i+(1<<k)-1<=n; i++){
                ls[t][k][i]=min(ls[t][k-1][i],ls[t][k-1][i+(1<<(k-1))]);
                rs[t][k][i]=max(rs[t][k-1][i],rs[t][k-1][i+(1<<(k-1))]);
            }
    }
    int Q;
    scanf("%d",&Q); 
    while(Q--){
        int x,y; scanf("%d%d",&x,&y);
        int l=x,r=x,ans=0;
        for(int i=17; i>=0; i--) {
            int tl=getl(i,l,r),tr=getr(i,l,r);
            if(y<tl||tr<y)l=tl,r=tr,ans|=1<<i;
        }
        printf("%d\n",ans>n?-1:ans+1);
        //发现一直到不了就寄了 
    }
    return 0;
}

 

$T4$

考场上写了个暴力,显然判定比较好想,而且如果不存在全部相等的话,那么路径是唯一的

那么这个子矩阵必然是一条链,那么只需要判断相邻数字是否连着就好了

 

上一篇:一. 八路彩灯自动控制


下一篇:剖析上线于【Coinlist】的【pSTAKE】的认识和使用方法