第14回ブルーブリッジカップjavaAグループ2023地方大会の予選大会へのソリューション

トピックPDFダウンロード:第14回蘭橋杯地方大会のPDFダウンロード

目次

質問 A: 特別な日

質問 B: AND OR XOR

質問 C: 平均的

質問 D: チェス盤

質問 E: 相互素数の数

質問 F: 階乗の合計

質問 G: シャオランの旅行計画

質問 H: 太陽

質問 I: タワー

質問 J: 逆 XOR 01 文字列


質問 A: 特別な日

質問の意味:指定された期間内で、年数が月数と日数の倍数であることを調べます。つまり、年 % 月 == 0 && 年 % 日 = = 0

アイデア: シミュレーション、私の答えは: 35813063

日数を数えるために使うもので、ブルーブリッジカップではこれがよく出てきます。

コード:

import java.util.*;
public class Main {
    static int pre[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
    public static void main(String[] args) {
        int sum=0;
        //i是年,j是月,k是日
        for(int i=2000;i<=2000000;i++){
            for(int j=1;j<=12;j++){
                int r=pre[j];
                if(j==2 && (i%400==0 || (i%100!=0 && i%4==0))) r++;
                //2月的闰年是29天
                for(int k=1;k<=r;k++){
                    if(i%j==0 && i%k==0) sum++;
                    if(i==2000000) {        //2000000年1月1号过后,直接跳出
                        i=2000001;
                        j=13;
                        break;
                    }
                }
            }
        }
        System.out.println(sum);
    }
}

質問 B: AND OR XOR

質問の意味:上から下に 5 つの数値を計算し、隣り合う 2 つの数値をペアとして計算します。演算子は &、|、または ^ です。これら 5 つの数字がわかれば、演算子は何個見つかるか、最終的な答えは 1 になります。

アイデア: dfs 検索、私の答え: 30528

dfs は、すべての演算子の配置 (pow(3,10)=59049) を列挙し、最終結果が 1 つの答えの数値 + 1 である場合、これら 5 つの数値を上から下に計算します。

コード (C++ で書かれた):

#include <bits/stdc++.h>
using namespace std;
int sum=0;
int a[100005];
int dp[10][10];
int mp[10][10];
void dfs(int d){
    if(d==11){
        for(int i=1;i<=4;i++) mp[1][i]=a[i];
        for(int i=1;i<=3;i++) mp[2][i]=a[i+4];
        for(int i=1;i<=2;i++) mp[3][i]=a[i+7];
        for(int i=1;i<=1;i++) mp[4][i]=a[10];
        for(int i=1;i<=4;i++){
            for(int j=1;j<=4-i+1;j++){
                if(mp[i][j]==0) dp[i][j]=dp[i-1][j]|dp[i-1][j+1];
                if(mp[i][j]==1) dp[i][j]=dp[i-1][j]^dp[i-1][j+1];
                if(mp[i][j]==2) dp[i][j]=dp[i-1][j]&dp[i-1][j+1];
            }
        }
        if(dp[4][1]==1)
            sum++;
        return;
    }
    a[d]=0;
    dfs(d+1);
    a[d]=1;
    dfs(d+1);
    a[d]=2;
    dfs(d+1);
}
int main()
{
    dp[0][1]=1;
    dp[0][2]=0;
    dp[0][3]=1;
    dp[0][4]=0;
    dp[0][5]=1;
    dfs(1);
    cout<<sum<<endl;
    return 0;
}

質問 C: 平均的

質問の意味:数値 (0 ~ 9 の範囲) とその変更コストが与えられた場合、最終的な数値 0 ~ 9 が同じになる最小コストを見つけてください。

アイデア: 貪欲

>n/10 の数値を他の数値に変更するだけです。つまり、>n/10 の数値を変更する必要があります (数量 -n/10)。もちろん、小さな変化も見つけてください

パス: アイデアの時間計算量は 100% になる可能性があります。具体的なパスは状況によって異なります。

コード:

import java.util.*;
public class Main{
	public static void main(String[] args){
		Scanner cin =new Scanner(System.in);
		int n=cin.nextInt();
		int A[][]=new int[11][100005];
		int len[]=new int[15];
		for(int i=1;i<=n;i++) {
			int x=cin.nextInt();
			int y=cin.nextInt();
			A[x][++len[x]]=y;
		}
		for(int i=0;i<10;i++) 
			Arrays.sort(A[i],1,len[i]+1);    //排序,不用list是因为没有板子,手敲不出来
		long sum=0;
		for(int i=0;i<10;i++) {
			for(int j=1;j<=len[i]-n/10;j++) {
				sum+=A[i][j];
			}
			
		}
		System.out.println(sum);
	}
}

質問 D: チェス盤

 

質問の意味: n*m 個のチェス盤は、最初は白い駒でいっぱいです。長方形を選択してすべてを反転し、最終的なチェス盤の状況を出力します。

アイデア: 差分プレフィックス合計

この四角形のすべての数値に 1 を加算し (最初はすべて 0 でした)、最終的に %2 が答えになります。

最大データは 2000 件のみであるため、変更される行のたびに差分が変更されます。合計実行数はわずか 2000*2000 です。

最後に、接頭語と行ごとにこれらの数字 %2 を印刷します。印刷時にスペースが入らないように注意してください。

パス: アイデアの時間計算量は 100% になる可能性があります。具体的なパスは状況によって異なります。

コード:

import java.util.*;
public class Main{
	public static void main(String[] args){
		Scanner cin =new Scanner(System.in);
		int n=cin.nextInt();
		int m=cin.nextInt();
		int A[][]=new int[2005][2005];
		for(int i=1;i<=m;i++) {
			int x1=cin.nextInt();
			int y1=cin.nextInt();
			int x2=cin.nextInt();
			int y2=cin.nextInt();
			for(int j=x1;j<=x2;j++) {
				A[j][y1]++;        //差分
				A[j][y2+1]--;
			}
		}
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=n;j++) {
				A[i][j]+=A[i][j-1];        //前缀和
				System.out.print(A[i][j]%2);
			}
			System.out.println();
		}
	}
}

質問 E: 相互素数の数

質問の意味: pow(a,b) と比較的素な 1-pow(a,b) にはいくつの数値がありますか

アイデア: 思考 + GCD + 高速パワー

答えは pow(a,b-1)*r です。r は 1-a において a と互いに素である量です。

そして a*a*a*a.... は互いに素、つまり a と互いに素な数です。

a と b の相互素数は gcd(a, b)==1 です。そして (a, b) と (a, b+a)、および (a, b+a*2)... は同じです。gcd() の式で確認できるはずです。

つまり、サイクルがあります。1-a を見てください。そして r はオイラー関数です

合格:

オイラー関数は 100% になる可能性があります

100% コード:

import java.util.*;
public class Main{
    static long mod=998244353;
    static long Euler(long n){        //求欧拉函数值
        long res=n;
        for(long i=2;i*i<=n;++i){
            if(n%i==0){
                res=res/i*(i-1);
                while(n%i==0)
                    n/=i;
            }
        }
        if(n>1)
            res-=res/n;
        return res;
    }
    static long qpow(long a,long b){
        long ans=1;
        while(b!=0){
            if(b%2==1)
                ans=ans*a%mod;
            a=a*a%mod;
            b/=2;
        }
        return ans;
    }
    public static void main(String[] args){
        Scanner cin =new Scanner(System.in);
        long a=cin.nextLong();
        long b=cin.nextLong();
        long r=Euler(a);        //1-a中有多少个数和a互质
        System.out.println(qpow(a,b-1)*(r%mod)%mod);
    }
}

質問 F: 階乗の合計

タイトルの意味:

アイデア: この質問にとってこれ以上のアイデアはありません。

最大の m を乱暴に計算する場合は、乗算剰余式のみを使用してください。

ans=1,2,6,24,120.... これらの階乗の合計が ans で割り切れるかどうかを計算します。つまり、次のように判断します。

A[1]!%ans+A[2]!%ans+....+A[n]!%ans==0

そうでない場合は、現在の ans に対応する階乗数を出力します。

合格:

この方法では上位 40% を通過できない可能性があることがわかりますが、ほとんどの場合、一部を通過することは可能です。

コード:

import java.util.*;
public class Main{
	public static void main(String[] args){
		Scanner cin =new Scanner(System.in);
		int n=cin.nextInt();
		int a[]=new int[n+10];
		for(int i=1;i<=n;i++) {
			a[i]=cin.nextInt();
		}
		long ans=1,p=1;
		while(true) {
			long sum=0;
			for(int i=1;i<=n;i++) {
				long res=1;
				for(int j=2;j<=a[i];j++) {
					res=res*j%ans;
				}
				sum=(sum+res)%ans;        //算和
			}
			if(sum==0) {
				ans*=(++p);
			}else {
				break;
			}
		}
		System.out.println(p-1);
	}
}

質問 G: シャオランの旅行計画

アイデア: 思考 + 優先キュー

以前に購入できるすべての単価と数量を記録し、石油がなくなったら最小価格を削除します。

100% のアイデアは、長さ 2 のリストを保存し、毎回最低価格をポップアップ表示し、その数量を変更する優先キューである必要があります。0になると押されなくなります。

コメントによれば、燃料タンクにはmの上限があるため、この考えは完全に正しいとは言えず、完全に正しい考えは思いつきません。

合格:

プライオリティ キューは、長さ 2 のリストを 100% 格納できます。

コード (優先キューの 60% が数量に応じて格納されます):

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner cin =new Scanner(System.in);
        PriorityQueue q=new PriorityQueue();
        int n=cin.nextInt();
        int m=cin.nextInt();
        long sum=0;
        for(int i=1;i<=n;i++) {
            int x=cin.nextInt();
            int y=cin.nextInt();
            int z=cin.nextInt();
            m-=x;
            while(m<0) {
                if(q.isEmpty()) {
                    sum=-1;
                    i=n+1;
                    break;
                }
                int r=(int)q.poll();
                sum+=r;
                m++;
            }
            for(int j=1;j<=z;j++) {
                q.add(y);
            }
        }
        System.out.println(sum);
    }
}

コード (優先キューは 100% 順番に格納されます):

import java.lang.reflect.Array;
import java.util.*;
public class Main{
    static ArrayList fun(int x,int y){
        ArrayList<Integer> t=new ArrayList();
        t.add(x);
        t.add(y);
        return t;
    }
    public static void main(String[] args){
        Scanner cin =new Scanner(System.in);
        PriorityQueue<ArrayList<Integer>> q=new PriorityQueue<>(new Comparator<ArrayList<Integer>>(){
            @Override
            public int compare(ArrayList<Integer> a, ArrayList<Integer> b){
                return a.get(0)-b.get(0);
            }
        });
        int n=cin.nextInt();
        int m=cin.nextInt();
        long sum=0;
        for(int i=1;i<=n;i++) {
            int x=cin.nextInt();
            int y=cin.nextInt();
            int z=cin.nextInt();
            m-=x;
            while(m<0) {
                if(q.isEmpty()) {
                    sum=-1;
                    i=n+1;
                    break;
                }
                List<Integer> r=q.poll();       //前面价格最小的
                int res=Math.min(r.get(1),-m);      //这次使用r的数量
                sum+=(long)res*r.get(0);
                m+=res;
                if(res!=r.get(1))       //要是还有剩余,将剩余数量再加入q中
                    q.add(fun(r.get(0),r.get(1)-res));
            }
            q.add(fun(y,z));
        }
        System.out.println(sum);
    }
}


質問 H: 太陽

 

タイトルの意味: いくつかの線分と光源を与えます。光源によって検出できる線分の数を決定します。

アイデア: 計算幾何学

100% の考えはなく、他人にブロックされているかどうかは 30%、つまりジャッジの 2 倍だけです。

ディスプレイが遮られる可能性があると判断し、少なくとも高さが遮られる <ファイル<光源または遮られる>ファイル> 光源が 遮られる可能性がある

次はブロックされた線分の 2 つの端点です。光源に接続した後はブロックされた線分と交差できません。

この方法は、外積を行って2 つの線分が交差していないことを確認することです。また、それらの線分が互いに素であるためには、2 つの点の同じ側にある必要があります。

合格:

このアイデアの時間計算量は 30% になる可能性があり、最適なのは極角ソートです。

コアコード:

    static double add(Node a,Node x,Node y) {        //叉积
        return (a.x-x.x)*(a.y-y.y)-(a.y-x.y)*(a.x-y.x);
    }
    static boolean solve(int i,int j) {
        //只有高度合适的时候,i才有可能被j挡到
        if((b[i]<=b[j] && b[j]<=y) || (y<=b[j] && b[j]<=b[i])) {
            double h1 = add(new Node(a[j], b[j]), new Node(a[i], b[i]), new Node(x, y));
            double h2 = add(new Node(a[j] + l[j], b[j]), new Node(a[i], b[i]), new Node(x, y));
            if ((h1 * h2) < 0) return true;
            h1 = add(new Node(a[j], b[j]), new Node(a[i] + l[i], b[i]), new Node(x, y));
            h2 = add(new Node(a[j] + l[j], b[j]), new Node(a[i] + l[i], b[i]), new Node(x, y));
            if ((h1 * h2) < 0) return true;
        }
        return false;
    }

コード:

import java.util.*;
public class Main{
    static int n,x,y;
    static int a[]=new int[1000000],b[]=new int[1000000],l[]=new int[1000000];
    static double add(Node a,Node x,Node y) {        //叉积
        return (a.x-x.x)*(a.y-y.y)-(a.y-x.y)*(a.x-y.x);
    }
    static boolean solve(int i,int j) {
        if((b[i]<=b[j] && b[j]<=y) || (y<=b[j] && b[j]<=b[i])) {
            double h1 = add(new Node(a[j], b[j]), new Node(a[i], b[i]), new Node(x, y));
            double h2 = add(new Node(a[j] + l[j], b[j]), new Node(a[i], b[i]), new Node(x, y));
            if ((h1 * h2) < 0) return true;
            h1 = add(new Node(a[j], b[j]), new Node(a[i] + l[i], b[i]), new Node(x, y));
            h2 = add(new Node(a[j] + l[j], b[j]), new Node(a[i] + l[i], b[i]), new Node(x, y));
            if ((h1 * h2) < 0) return true;
        }
        return false;
    }
    public static void main(String[] args){
        Scanner cin =new Scanner(System.in);
        n=cin.nextInt();
        x=cin.nextInt();
        y=cin.nextInt();
        for(int i=1;i<=n;i++) {
            a[i]=cin.nextInt();
            b[i]=cin.nextInt();
            l[i]=cin.nextInt();
        }
        int sum=0;
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                if(i!=j && solve(i,j)) {
                    break;
                }
                if(j==n) sum++;
            }
        }
        System.out.println(sum);
    }
}
class Node{
    public double x,y;
    public Node(){}
    public Node(double a,double b){
        this.x=a;
        this.y=b;
    }
}

質問 I: タワー

 サンプルがうまくいかない、何もない

合格: 0%

質問 J: 逆 XOR 01 文字列

タイトルの意味:

アイデア: 接頭辞の合計 + 回文文字列判定

この種の質問の考え方は、注意深く研究した後では間違っているでしょう

私の考え: 反 XOR 演算の後、文字列は 0 を中心とする奇数長の回文文字列、または偶数長の回文文字列のみになります。操作は 1 つだけで、他の操作は両側に 0 と 1 を加算するものである場合、回文文字列は指定された文字列内に存在する必要があります。

そのため、その中のテキスト部分文字列を見つけるには、中央展開方法を使用します。1 の数=左端点の左側にある 1 の数 + 右端点の右側にある 1 の数 + 回文文字列内の 1 の数 /2 を使用します。

すべてのケースで 1 の最小数を見つけるには、O (n の 2 乗) になります。

合格:

時間計算量は O(n 二乗)、つまり 60% です。

O (n) または O (nlogn) で回文の部分文字列 (最長ではない) を見つける方法があるかどうかはわかりません。もちろん、この考えが正しいという前提があります。

コード:

import java.util.*;
public class Main{
    static String s;
    static int f[]=new int[1000000];
    static int n,mi=10000000;
    static int get(int x,int y){        //前缀和
        if(y<1 || x>n || x>y) return 0;
        return f[y]-f[x-1];
    }
    static void solve(int l,int r){
        while(l-1!=0 && r+1!=n+1 && s.charAt(l-1)==s.charAt(r+1)){
            l--;
            r++;
        }
        mi=Math.min(mi,get(l,r)/2+get(1,l-1)+get(r+1,n));
    }
    public static void main(String[] args){
        Scanner cin =new Scanner(System.in);
        s=cin.next();
        n=s.length();
        s=" "+s;
        for(int i=1;i<=n;i++){
            f[i]+=f[i-1]+(s.charAt(i)=='1'?1:0);
        }
        for(int i=1;i<=n;i++){
            if(s.charAt(i)=='0')
                solve(i,i);
            if(i!=n && s.charAt(i)==s.charAt(i+1))
                solve(i,i+1);
        }
        System.out.println(mi);
    }
}

おすすめ

転載: blog.csdn.net/m0_58177653/article/details/130035429