【题目记录】——ICPC焦作2018


题目集地址 ICPC焦作2018

A Xu Xiake in Henan Province 签到

题目大意:t 组查询,每组给出 4 个数,代表去过 4 个地方,如果 4 个数都是 0,输出 Typically Otaku,如果有 3 个 0,输出 Eye-opener,如果有 2 个 0,输出 Young Traveller,如果有 1 个 0,输出 Excellent Traveller,如果没有 0,输出 Contemporary Xu Xiake
思路:简单模拟
AC代码:

#include <bits/stdc++.h>
using namespace std;
string res[] = {"Typically Otaku","Eye-opener","Young Traveller","Excellent Traveller","Contemporary Xu Xiake"};
signed main(){
    int t;
    cin>>t;
    while(t--){
        int cnt = 0;
        for(int i = 0 ; i < 4 ; i ++){
            int x; cin>>x;
            cnt += (x > 0);
        }
        cout<<res[cnt]<<endl;
    }
}

B Ultraman vs. Aodzilla and Bodzilla 思维

题目地址B Ultraman vs. Aodzilla and Bodzilla
题目大意:两只怪兽,它们的生命和攻击分别为hpA,hpB,attA,attB,现在你要打败它们,第i回合你的攻击为i,每个回合,奥特曼首先受到剩余怪兽攻击值之和的攻击。问在承受伤害最少的前提下,攻击序列字典序最小是怎样的。攻击A就是A,攻击B就是B。最后输出承受的最小伤害和攻击序列。
思路:首先要确保受到的攻击最小,一定是优先杀死一个怪兽才会使得受到的总攻击最小,因为要保证最后的字典序也最小,那么就先考虑优先杀死A。

我们预处理1-10000的前缀和使用二分的方法找到杀死两只怪兽的最小回合数,同时会有一个攻击力的溢出值记为overflow1,然后找到杀死A的最小回合数,杀死A的时候会得到一个攻击力的溢出值记为overflow2,

如果overflow1>overflow2说明优先杀死A的方案需要再加一个回合。然后计算出优先杀死A的伤害最小值。优先杀死B的伤害最小值也计算出来二者取最小值输出即可,如果收到的伤害是一样的则输出优先杀死A的方案
AC代码:

#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=100002;
int T,sum[maxn];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    for(int i=1; i<=maxn-1; i++)
        sum[i]=sum[i-1]+i;
    cin >>T;
    while(T--) {
        int hpa,hpb,atka,atkb;
        cin >>hpa>>hpb>>atka>>atkb;
        string A,B;
        int pos=lower_bound(sum+1,sum+maxn,hpa)-sum;
        //找到第一个大于等于A生命值的位置
        int last=lower_bound(sum+1,sum+maxn,hpa+hpb)-sum;
        //找到第一个大于等于A+B生命值的位置
        int rest=sum[pos]-hpa,atkA=last*atkb+pos*atka;
        //大于等于A生命值位置还能剩下的,总攻击力
        if(sum[last]-sum[pos]>=hpb)
            //如果last到pos间可以消灭B
            for(int i=1; i<=pos; i++)A+='A';
        else
            for(int i=1; i<=pos; i++)
                if(i!=rest)A+='A';
                else A+='B';//把超出A的值分给B
        for(int i=pos+1; i<=last; i++)A+='B';//剩下的部分都给B

        pos=lower_bound(sum+1,sum+maxn,hpb)-sum;
        //找到第一个大于等于B的位置
        rest=sum[pos]-hpb;//超过B剩下的
        int pos2=upper_bound(sum+1,sum+maxn,rest)-sum-1;
        //查找超过B剩下的位置,代表这前面可以放A,因为是upper_bound所以-1
        int rest2=sum[last]-sum[pos];
        //pos之后的和
        int atkB=last*atka+pos*atkb;//总攻击力
        if(rest2+sum[pos2]>=hpa) {//所有预定放A的位置
            for(int i=1; i<=pos2; i++)B+='A';
            for(int i=pos2+1; i<=pos; i++)B+='B';
            for(int i=pos+1; i<=last; i++)B+='A';
        } else {
            int left=hpa-rest2;//A缺的部分
            for(int i=1; i<=pos; i++)
                if(left-i>i||left==i) {
                //left-i>i代表后面还能减,left==i代表正好是这个数
                    left-=i;
                    B+='A';
                } else
                    B+='B';
            for(int i=pos+1; i<=last; i++)B+='A';
        }

        if(atkB<atkA)
            cout <<atkB<<" "<<B;
        else if(atkB>atkA)
            cout <<atkA<<" "<<A;
        else
            cout <<atkA<<" "<<min(A,B);
        cout <<endl;
    }
    return 0;
}

E Resistors in Parallel (思维+数学)

题目地址E Resistors in Parallel
题目大意:有n个电阻,编号从1到n,现在从这n个电阻中选择一部分电阻并联,想要最后并联出来的电阻阻值最小,对于编号为i的电阻来说,其阻值满足以下条件:
在这里插入图片描述
对这n个电阻,每个编号i对应有一个集合 S i S_i Si(集合间可能相交),集合内的元素如下
在这里插入图片描述
现在要从n个编号的对应集合中选择一个方案,将涉及到的电阻全部并联,求出并联可以得到的阻值最小值,用分数表示,多组输入, 1 ≤ n ≤ 1 0 100 1\le n\le 10^{100} 1n10100
思路:数据量很大应该是找一个规律或者有数学公式,然后这个数据量用到了大数,用c++写一个大数或者java的大数类都可以,我们用的是java的大数类

我们打了前31000组结果发现一个规律,可以发现对于每个n来说,设选取的集合编号是p,p为多个质数的乘积且刚好小于n,结果的分子为质数积,分母为质数+1的积,由于n只到100位,所以只需要几十个质数即可。
我们是先用C++打出了前100多个质数然后放到java里面直接用了。
请添加图片描述
然后就是要注意java语言提交的格式,类名必须是Main,还有就是n为1的时候特判输出"1/1"而不是"1"我们在这错了几发。

import java.math.BigInteger;
import java.util.Scanner;

public class Main{

	static int Prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223};
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);
		int n;
		n=input.nextInt();
		for(int i = 1;i <= n;i++)
		{
			String string=input.next();
			BigInteger Int1 = new BigInteger(string);
			if(Int1.compareTo(new BigInteger("1"))==0)
			{
				System.out.println("1/1");
			}
			else {
				BigInteger u=new BigInteger("1");
				BigInteger d=new BigInteger("1");
				for(int j = 0;j<=150;j++)
				{
					BigInteger t=new BigInteger(""+Prime[j]);
					u=u.multiply(t);
					d=d.multiply(t.add(new BigInteger("1")));
					if(u.multiply(new BigInteger(""+Prime[j+1])).compareTo(Int1)==1)
					{
						break;
					}
				}
				BigInteger _gcd=u.gcd(d);
				u=u.divide(_gcd);
			    d=d.divide(_gcd);
				System.out.println(u+"/"+d);
			}
		}
	}
}

I Distance (思维)

题目地址I Distance
题目大意:在一个数轴上有n个点,从左到右分布,编号也一样,给出第i个点与第i+1个点的距离 a i a_i ai ,对于1到n的每个整数k,在数轴上选择k个不同的给定点,最大化该点到其余所有点的距离和。
思路:贪心的去考虑放置策略,首先将各点坐标根据 a i a_i ai求出来,每次放最左端和最右端的两个端点,轮流放置,并且记录已经增加的和,每次放新的点相当于将一个点放入了多个区间,如果新点是右端点,还需要加上和左端点的距离。
AC代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define int long long
using namespace std;
const int maxn=2e5+5;
int T,n,a[maxn];
signed main() {
    scanf("%lld",&T);
    while(T--) {
        scanf("%lld",&n);
        for(int i=2; i<=n; i++) {
            int x;
            scanf("%lld",&x);
            a[i]=a[i-1]+x;//构造坐标
        }
        int low=1,high=n,sum=0,pre=0;
        for(int i=1; i<=n; i++)
            if(i==1)printf("0");
            else if(i==2)printf(" %lld",pre=sum=-a[low++]+a[high--]);
            //第一次特判
            else {
                if(i&1)//如果是奇数,放的是左端点,直接加区间和
                    sum+=pre;
                else
                    sum+=pre-a[low]+a[high],pre+=a[high--]-a[low++];
                    //放的右端点,加上区间和和左右间距,更新区间和
                printf(" %lld",sum);
            }
        putchar('\n');
    }
    return 0;
}

H(后缀数组)(待补)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值