2019, XII Samara Regional Intercollegiate Programming (G、H、J、K、L题解)

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
 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){
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\n");
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\n",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

否则y的第一位为0且y在二进制第一位为0的这些数中.

像这样每轮查询下去,每轮查的次数都为上一次的一半,

也就是总次数q=n+n/2+n/4+n/8+...<=2*n.

最后可以即可确定y的值

具体实现见代码:

 1 #define bug(x) cout<<#x<<" is "< 2 #define IO std::ios::sync_with_stdio(0);
 3 #include 
 4 #define iter ::iterator
 5 using namespace  std;
 6 typedef long long ll;
 7 typedef pairP;
 8 typedef pairP1;
 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<<"? "<" "<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<<"! "<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 "< 2 #define IO std::ios::sync_with_stdio(0);
 3 #include 
 4 #define iter ::iterator
 5 using namespace  std;
 6 typedef long long ll;
 7 typedef pairP;
 8 typedef pairP1;
 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"%d ",k);
37         else printf("%d\n",k);
38     }
39 }

K题

https://codeforces.com/gym/102215/problem/K

题意

有n个卡片,卡片共有RGB三种颜色,现在有两个容器,现在按照给定的顺序发卡片,每次选择把卡片发到一个容器的顶端,要你保证最后两个容器头尾拼接起来后所有的颜色的卡片连在一起。

如果能实现输出YES否则就是NO

思路

模拟。

分两种情况:

1、碰到的第一种卡片和第二种卡片放在同一个容器;

2、碰到的第一种卡片和第二种卡片放在两个不同容器。

  1 #define bug(x) cout<<#x<<" is "<  2 #define IO std::ios::sync_with_stdio(0);
  3 #include 
  4 #define iter ::iterator
  5 using namespace  std;
  6 typedef long long ll;
  7 typedef pairP;
  8 typedef pairP1;
  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){
 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\n");
 42         exit(0);
 43     }
 44     int flag=0;
 45     for(int i=id+1;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){
 59             x=cal(s[i]);
 60             if(!(x==l||x==r))break;
 61         }
 62         if(i==len){
 63             printf("YES\n");
 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\n");
 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\n");
 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){
 96         x=cal(s[i]);
 97         if(!a[x]){
 98             id=i;
 99             break;
100         }
101     }
102     if(id==-1){
103         printf("YES\n");
104         return 0;
105     }
106     int k=cal(s[id]);
107     int flag=0;
108     for(int i=id+1;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\n");
122         return 0;
123     }
124     int flag1=0;
125     for(int i=id+1;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){
135         x=cal(s[i]);
136         if(!(x==l||x==r)){
137             printf("NO\n");
138             return 0;
139         }
140     }
141     printf("YES\n");
142 }

 

L题

https://codeforces.com/gym/102215/problem/L

题意

给定两个相交的圆A和圆B,求在相交区域中能放下的最大的圆的半径和圆心。

思路

其实就是个简单高中数学题。

不难想到内切圆肯定是最大的,然后我们可以先求半径r,

如图:

2019, XII Samara Regional Intercollegiate Programming (G、H、J、K、L题解)_第1张图片

由于对称性,内切圆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 "< 2 #define IO std::ios::sync_with_stdio(0);
 3 #include 
 4 #define iter ::iterator
 5 using namespace  std;
 6 typedef long long ll;
 7 typedef pairP;
 8 typedef pairP1;
 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\n",ans0,ans1,ans2);
31 }

 

你可能感兴趣的:(2019, XII Samara Regional Intercollegiate Programming (G、H、J、K、L题解))