C~F Java题解,代码已去除冗余

C 小红打怪

假设a次可以打完,那么多打一次更可以打完,因此答案满足二段性,二分即可。。在check的时候,可以假设最初全部进行了全打击,先保证不浪费的情况下进行相邻打击,在用单点打击处理残局,最后在进行一波相邻打击首尾,check返回真的条件为全体不大于0,时间复杂度O(nlogC),其中C==1e9

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        long a[]=new long[n+5];
        for(int i=0;i<n;i++){
            a[i]=sc.nextInt();
        }
        int l=0,r=(int)1e9;
        while(l<r){
            int mid=(l+r)>>1;
            if(check(a.clone(),mid)){
                r=mid;
            }
            else{
                l=mid+1;
            }
            if(l==r-1){
                if(check(a.clone(),l)){
                    r=l;
                }
                break;
            }
        }
        System.out.println(r);
    }
    static boolean check(long a[],int max){
        for(int i=0;i<a.length;i++){
            a[i]-=max;
        }
        long p1=max,p2=max;
        for(int i=0;i<a.length-1;i++){
            if(Math.min(a[i],a[i+1])>0){
                long p=Math.min(p1,Math.min(a[i],a[i+1]));
                p1-=p;
                a[i]-=p;
                a[i+1]-=p;
            }
        }
        for(int i=0;i<a.length;i++){
            if(a[i]>0){
                long p=Math.min(p2,a[i]);
                p2-=p;
                a[i]-=p;
            }
        }
        for(int i=0;i<a.length-1;i++){
            if(a[i]>0){
                long p=Math.min(p1,a[i]);
                p1-=p;
                a[i]-=p;
                a[i+1]-=p;
            }
        }
        for(long b:a){
            if(b>0){
                return false;
            }
        }
        return true;
    }
}

D 小红开锁

分层进行模拟即可,指针最大运行两倍圈的长度,时间复杂度O(n^2)

import java.util.*;
public class Main{
   static int move[][]={{0,1},{1,0},{0,-1},{-1,0}};
   public static void main(String args[]){
       Scanner sc=new Scanner(System.in);
       int n=sc.nextInt(),count[]=new int[4],ans=(int)1e9;
       String s[]=new String[n*2];
       for(int i=0;i<n*2;i++){
           s[i]=sc.next();
       }
       for(int i=0;i<n;i++){
           int dx=i,dy=i,idx=0,pos[][]={{i,n-1},{n-1,n*2-1-i},{n*2-1-i,n},{n,i}};
           while(true){
               char c=s[dx].charAt(dy);
               while(true){
                   int x=dx+move[idx][0],y=dy+move[idx][1];
                   if(Math.min(Math.min(x,n*2-1-x),Math.min(y,n*2-1-y))!=i){
                       idx=(idx+1)%4;
                   }
                   else{
                       dx=x;
                       dy=y;
                       break;
                   }
               }
               if(c=='X'&&s[dx].charAt(dy)=='O'){
                   dx-=move[idx][0];
                   dy-=move[idx][1];
                   break;
               }
           }
           for(int found=0,steps=0;found<4;steps++){
               for(int j=0;j<4;j++){
                   if(dx==pos[j][0]&&dy==pos[j][1]){
                       found++;
                       count[j]+=steps;
                       break;
                   }
               }
               while(true){
                   int x=dx+move[idx][0],y=dy+move[idx][1];
                   if(Math.min(Math.min(x,n*2-1-x),Math.min(y,n*2-1-y))!=i){
                       idx=(idx+1)%4;
                   }
                   else{
                       dx=x;
                       dy=y;
                       break;
                   }
               }
           }
       }
       for(int a:count){
           ans=Math.min(ans,a);
       }
       System.out.println(ans);
   }
}

E 小红开宝箱

方法一:二分图最大匹配为题,敲击次序和敲击对象可构成二分图,按照匈牙利算法进行,时间复杂度O(n^2),由于本题是找到无法匹配的时候立即终止,因此复杂度跑不满

import java.util.*;
import java.io.*;
public class Main{
    public static void main(String args[]) throws IOException{
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
        int n=Integer.parseInt(bf.readLine()),idx[]=new int[n+5],ans[]=new int[n+5];
        List<Integer> path[]=new List[n+5];
        for(int i=1;i<=n;i++){
            path[i]=new ArrayList<>();
            String arr[]=bf.readLine().split(" ");
            for(int j=Integer.parseInt(arr[0]);j>0;j--){
                path[i].add(Integer.parseInt(arr[j]));
            }
        }
        for(int i=1;i<=n;i++){
            if(!find(path,idx,new HashSet<>(),i)){
                bw.write("kou is angry");
                bw.flush();
                return;
            }
        }
        for(int i=1;i<=n;i++){
            ans[idx[i]]=i;
        }
        for(int i=1;i<=n;i++){
            bw.write(ans[i]+" ");
        }
        bw.flush();
    }
    static boolean find(List<Integer> path[],int idx[],Set<Integer> set,int k){
        for(int a:path[k]){
            if(!set.contains(a)){
                set.add(a);
                if(idx[a]==0||find(path,idx,set,idx[a])){
                    idx[a]=k;
                    return true;
                }
            }
        }
        return false;
    }
}

方法二:搜索(待写)

F 小红闯地下城

分别计算用了传送和不用的最大遍历点数,取最大值,其中在计算后者的时候,需要避免同组先点的重复计算,时间复杂度O(nlogC),其中C==19

import java.util.*;
import java.io.*;
public class Main{
    public static void main(String args[]) throws IOException{
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
        String arr[]=bf.readLine().split(" ");
        int n=Integer.parseInt(arr[0]),x=Integer.parseInt(arr[1]),a[]=new int[n+5],ans=Math.min(n,(x+2)/3),level[]=new int[n+5],p[][]=new int[n+5][19];
        List<Integer> path[]=new List[n+5];
        arr=bf.readLine().split(" ");
        for(int i=1;i<=n;i++){
            a[i]=Integer.parseInt(arr[i-1]);
            path[i]=new ArrayList<>();
        }
        for(int i=0;i<n-1;i++){
            arr=bf.readLine().split(" ");
            int u=Integer.parseInt(arr[0]),v=Integer.parseInt(arr[1]);
            path[u].add(v);
            path[v].add(u);
        }
        Queue<Integer> q=new LinkedList<>();
        q.add(1);
        level[1]=1;
        while(!q.isEmpty()){
            int b=q.poll();
            for(int c:path[b]){
                if(level[c]==0){
                    p[c][0]=b;
                    level[c]=1+level[b];
                    q.add(c);
                }
            }
        }
        for(int i=1;i<19;i++){
            for(int j=1;j<=n;j++){
                p[j][i]=p[p[j][i-1]][i-1];
            }
        }
        for(int i=1;i<=n;i++){
            int lca=find(i,a[i],p,level),num=level[i]+level[a[i]]-level[lca],cost=num+level[i]+level[a[i]]-1;
            //经过点数:num=level[i]+level[a[i]]-level[lca]
            //花费的时间num+1+来去的距离;
            if(cost<=x){
                ans=Math.max(ans,num+Math.min((x-cost)/3,n-num));
            }
        }
        bw.write(ans+"\n");
        bw.flush();
    }
    static int find(int a,int b,int p[][],int level[]){
        if(level[a]<level[b]){
            return find(b,a,p,level);
        }
        for(int i=18;i>=0;i--){
            if(level[a]-level[b]>=1<<i){
                a=p[a][i];
            }
        }
        for(int i=18;i>=0;i--){
            if(p[a][i]!=p[b][i]){
                a=p[a][i];
                b=p[b][i];
            }
        }
        return a==b?a:p[a][0];
    }
}