文章目录
题目集地址 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}
1≤n≤10100
思路:数据量很大应该是找一个规律或者有数学公式,然后这个数据量用到了大数,用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;
}