G题
https://codeforces.com/gym/102215/problem/G
题意
有一天黑客德米特里发现了一个有趣的游戏叫Akinator。这个游戏的规则很简单:玩家想一些名人,阿克熄灯器试着在有限的几个问题中猜出这个人。每个回合熄灯器都会问玩家:“这个人是i1还是i2 ?”还是我?(这里m是问题中的人数,每个问题的人数都不一样)。玩家回答“是”或“不是”,阿克熄灯器将使用获得的信息。一旦阿克熄灯器认识了这个有思想的人,他就说出了答案,赢了。如果熄灯者不能在给定的问题中猜出这个人,他就输了。
熄灯器有k个问题来猜测这个有思想的人。我们事先知道德米特里想到了这n个人中的一个:1,2,…, n的概率为p1 p2…pn。确定Akinator是否能够保证他在k个问题中猜对,如果他能够,他需要的最小平均问题数量是多少。
输入
第一行包含两个整数n和k(1≤k≤n≤100)——可能思考的人数和问题的数量。
第二行包含n个整数a1 a2…an(1≤ai≤1012)概率与ai成正比,即可以计算为。
输出
如果阿克熄灯器不能肯定地猜出有思想的人,输出«No solution»。
否则输出不可约分数——Akinator赢得游戏所需的最小平均问题数。
思路
区间dp。
d[l][r][k]表示第k轮[l,r]区间的最小值。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 const LL INF = 1e15; 6 7 int n,k; 8 9 LL memo[105][105][105]; 10 LL li[105]; 11 12 LL dp(int l, int r, int step){ 13 if(step > k) return INF; 14 if(l==r) return step*li[l]; 15 LL &ret = memo[l][r][step]; 16 if(ret!=-1) return ret; 17 ret = INF; 18 for(int i=l;i<r;i++){ 19 ret = min(ret,dp(l,i,step+1)+dp(i+1,r,step+1)); 20 } 21 return ret; 22 } 23 24 int main(){ 25 scanf("%d%d",&n,&k); 26 int tmp = ceil(log2(n)); 27 if(k < tmp){ 28 printf("No solution "); 29 return 0; 30 } 31 LL tot = 0LL; 32 memset(memo,-1,sizeof(memo)); 33 for(int i=1;i<=n;i++) scanf("%lld",&li[i]), tot+=li[i]; 34 sort(li+1,li+n+1); 35 LL ans = dp(1,n,0); 36 LL tmp1 = __gcd(ans,tot); 37 ans/=tmp1; 38 tot/=tmp1; 39 printf("%lld/%lld ",ans,tot); 40 return 0; 41 }
H题
https://codeforces.com/gym/102215/problem/H
题意
一个数组里有0到n一共n+1个数,现在某一个数丢失,让你通过q次询问,每次询问第i个数的二进制数中第j位(从低到高)的结果,找出丢失的数。
思路
交互题。
我们先思考一个问题,如果一个数丢失,他会对整个数组造成什么影响?
首先我们考虑第一轮查询,我们直接查询1到n个数中每个数二进制第一位的结果。
由于原数组是0到n,所以每个数的二进制第一位为1的数量x加起来肯定是(n+1)/2.
设丢失的数为y,
如果查询到的数量b<x,y的第一位为1且y在二进制第一位为1的这些数中,
否则y的第一位为0且y在二进制第一位为0的这些数中.
像这样每轮查询下去,每轮查的次数都为上一次的一半,
也就是总次数q=n+n/2+n/4+n/8+...<=2*n.
最后可以即可确定y的值
具体实现见代码:
1 #define bug(x) cout<<#x<<" is "<<x<<endl; 2 #define IO std::ios::sync_with_stdio(0); 3 #include <bits/stdc++.h> 4 #define iter ::iterator 5 using namespace std; 6 typedef long long ll; 7 typedef pair<ll,ll>P; 8 typedef pair<P,P>P1; 9 #define pb push_back 10 #define se second 11 #define fi first 12 #define rs o<<1|1 13 #define ls o<<1 14 #define inf 0x3f3f3f3f3f3f3f3f 15 const int N=1e5+5; 16 set<int>s; 17 int n; 18 int main(){ 19 cin>>n; 20 for(int i=1;i<=n;i++){ 21 s.insert(i); 22 } 23 set<int> iter it; 24 int res=0,ans=0,b=0; 25 for(int i=0;s.size()>=1;i++){ 26 int m=s.size(); 27 b=0; 28 set<int>temp; 29 30 for(it=s.begin();it!=s.end();it++){ 31 int x=*it; 32 cout<<"? "<<x<<" "<<i<<endl; 33 cout.flush(); 34 cin>>res; 35 if(res){ 36 temp.insert(x); 37 b++; 38 } 39 } 40 if(b<(m+1)/2){ 41 ans+=(1<<i); 42 s=temp; 43 } 44 else{ 45 for(it=temp.begin();it!=temp.end();it++){ 46 int x=*it; 47 s.erase(x); 48 } 49 } 50 51 } 52 cout<<"! "<<ans<<endl; 53 cout.flush(); 54 }
J题
https://codeforces.com/gym/102215/problem/J
题意
在绝地武士锦标赛上,n名绝地武士正在互相厮杀。每个绝地武士都有三个标准:力量、敏捷和智慧。当两名绝地武士进行战斗时,获胜的绝地武士至少比他对手的参数高两个参数。例如,带参数的绝地武士(5,9,10)击败带参数的绝地武士(2,12,4),原因是第一个和第三个参数。
西斯有个计划要把一个绝地变成原力的黑暗面。这将给他提供一种强大的技能,允许他在每次战斗前任意设置所有参数,限制是参数应该保持非负整数,并且它们的和不应该改变。
对于每一个绝地武士,决定他能打败多少其他绝地武士转向黑暗势力。
思路
贪心。
把所有人的最小的两个维度值的和的结果存起来从小到大排序,二分查找当前这个人总能力值-1在数组中的位置,输出即可,注意答案要-1,因为自己不能打自己。
1 #define bug(x) cout<<#x<<" is "<<x<<endl; 2 #define IO std::ios::sync_with_stdio(0); 3 #include <bits/stdc++.h> 4 #define iter ::iterator 5 using namespace std; 6 typedef long long ll; 7 typedef pair<ll,ll>P; 8 typedef pair<P,P>P1; 9 #define pb push_back 10 #define se second 11 #define fi first 12 #define rs o<<1|1 13 #define ls o<<1 14 #define inf 0x3f3f3f3f3f3f3f3f 15 const int N=5e5+5; 16 int n,cnt; 17 ll a[N],b[N],d[N]; 18 ll c[N]; 19 int main(){ 20 scanf("%d",&n); 21 for(int i=1;i<=n;i++){ 22 scanf("%d%d%d",&a[1],&a[2],&a[3]); 23 c[i]=a[1]+a[2]+a[3]-1; 24 sort(a+1,a+4); 25 ll x=a[1]+a[2]; 26 b[++cnt]=x; 27 if(a[3]<=1)d[i]=1; 28 } 29 sort(b+1,b+1+cnt); 30 for(int i=1;i<=n;i++){ 31 int k=upper_bound(b+1,b+1+cnt,c[i]-1)-b; 32 if(k==cnt+1)k--; 33 if(b[k]>=c[i])k--; 34 k--; 35 if(d[i])k++; 36 if(i<n)printf("%d ",k); 37 else printf("%d ",k); 38 } 39 }
K题
https://codeforces.com/gym/102215/problem/K
题意
有n个卡片,卡片共有RGB三种颜色,现在有两个容器,现在按照给定的顺序发卡片,每次选择把卡片发到一个容器的顶端,要你保证最后两个容器头尾拼接起来后所有的颜色的卡片连在一起。
如果能实现输出YES否则就是NO
思路
模拟。
分两种情况:
1、碰到的第一种卡片和第二种卡片放在同一个容器;
2、碰到的第一种卡片和第二种卡片放在两个不同容器。
1 #define bug(x) cout<<#x<<" is "<<x<<endl; 2 #define IO std::ios::sync_with_stdio(0); 3 #include <bits/stdc++.h> 4 #define iter ::iterator 5 using namespace std; 6 typedef long long ll; 7 typedef pair<ll,ll>P; 8 typedef pair<P,P>P1; 9 #define pb push_back 10 #define se second 11 #define fi first 12 #define rs o<<1|1 13 #define ls o<<1 14 #define inf 0x3f3f3f3f3f3f3f3f 15 const int N=1e5+5; 16 char s[N]; 17 int cal(int x){ 18 if(x=='R')return 1; 19 else if(x=='G')return 2; 20 return 3; 21 } 22 int a[10],l,r,l1,r1; 23 void work(int t,int len){//第一种情况:首先碰到的两个放在同一堆 24 int x=cal(s[t]); 25 l=x; 26 int id=-1; 27 for(int i=t+1;i<len;i++){ 28 x=cal(s[i]); 29 if(x==l1){ 30 id=i; 31 r1=r=x; 32 break; 33 } 34 if(x!=l){ 35 id=i; 36 r1=r=x; 37 break; 38 } 39 } 40 if(id==-1){ 41 printf("YES "); 42 exit(0); 43 } 44 int flag=0; 45 for(int i=id+1;i<len;i++){ 46 x=cal(s[i]); 47 if(x==l||x==r)continue; 48 if(r1==l1){ 49 flag++; 50 break; 51 } 52 id=i; 53 r=x; 54 break; 55 } 56 if(!flag){ 57 int i; 58 for(i=id+1;i<len;i++){ 59 x=cal(s[i]); 60 if(!(x==l||x==r))break; 61 } 62 if(i==len){ 63 printf("YES "); 64 exit(0); 65 } 66 } 67 68 } 69 int main(){ 70 scanf("%s",s); 71 int len=strlen(s); 72 if(len<=3){ 73 printf("YES "); 74 return 0; 75 } 76 int x=cal(s[0]); 77 l1=l=x; 78 int tl=l; 79 a[x]++; 80 int p=0; 81 while(p<len){ 82 if(cal(s[p])!=x)break; 83 p++; 84 } 85 if(p==len){ 86 printf("YES "); 87 return 0; 88 } 89 work(p,len); 90 l1=l=tl; 91 x=cal(s[p]); 92 r1=r=x; //第二种情况:首先碰到的两个分别放在两堆 93 a[x]++; 94 int id=-1; 95 for(int i=p;i<len;i++){ 96 x=cal(s[i]); 97 if(!a[x]){ 98 id=i; 99 break; 100 } 101 } 102 if(id==-1){ 103 printf("YES "); 104 return 0; 105 } 106 int k=cal(s[id]); 107 int flag=0; 108 for(int i=id+1;i<len;i++){ 109 x=cal(s[i]); 110 if(a[x]){ 111 flag++; 112 id=i; 113 if(x==l){ 114 r=k; 115 } 116 else l=k; 117 break; 118 } 119 } 120 if(!flag){ 121 printf("YES "); 122 return 0; 123 } 124 int flag1=0; 125 for(int i=id+1;i<len;i++){ 126 x=cal(s[i]); 127 if(x==l||x==r)continue; 128 flag1++; 129 if(l1==x)r=x; 130 else if(r1==x)l=x; 131 id=i; 132 break; 133 } 134 for(int i=id+1;i<len;i++){ 135 x=cal(s[i]); 136 if(!(x==l||x==r)){ 137 printf("NO "); 138 return 0; 139 } 140 } 141 printf("YES "); 142 }
L题
https://codeforces.com/gym/102215/problem/L
题意
给定两个相交的圆A和圆B,求在相交区域中能放下的最大的圆的半径和圆心。
思路
其实就是个简单高中数学题。
不难想到内切圆肯定是最大的,然后我们可以先求半径r,
如图:
由于对称性,内切圆D的圆心D肯定在AB上;
设圆A半径为r1,圆B半径为r2,则有:
AC=AB-r2;
CF=r1-AC;
r=CD=CF/2;
求出r后,我们已经知道线段AB和AD=AC+r,求D点直接根据线段比例和向量搞一搞不就出来了。
具体见代码:
1 #define bug(x) cout<<#x<<" is "<<x<<endl; 2 #define IO std::ios::sync_with_stdio(0); 3 #include <bits/stdc++.h> 4 #define iter ::iterator 5 using namespace std; 6 typedef long long ll; 7 typedef pair<ll,ll>P; 8 typedef pair<P,P>P1; 9 #define pb push_back 10 #define se second 11 #define fi first 12 #define rs o<<1|1 13 #define ls o<<1 14 #define inf 0x3f3f3f3f3f3f3f3f 15 const int N=1e5+5; 16 double x[3],y[3],r[3]; 17 int main(){ 18 for(int i=1;i<=2;i++){ 19 cin>>x[i]>>y[i]>>r[i]; 20 } 21 double dx=x[2]-x[1]; 22 double dy=y[2]-y[1]; 23 double d=sqrt(dx*dx+dy*dy); 24 25 double g=d-r[2]; 26 double ans2=(r[1]-g)/2; 27 g+=ans2; 28 double ans0=x[1]+dx*g/d; 29 double ans1=y[1]+dy*g/d; 30 printf("%.15lf %.15lf %.15lf ",ans0,ans1,ans2); 31 }