【题解】Codeforces GYM 2020 ICPC Asia Seoul Regional

【B. Commemorative Dice】

暴力枚举36种情况,对得到的答案约分

 1 #include<cstdio>
 2 using namespace std;
 3 
 4 int n,a[10],b[10],ans,x;
 5 
 6 inline int gcd(int x,int y) {return x%y?gcd(y,x%y):y;}
 7 int main()
 8 {
 9     n=6;
10     for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
11     for (int i=1; i<=n; ++i) scanf("%d",&b[i]);
12     for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) if (a[i]>b[j]) ans++;
13     x=gcd(ans,36);
14     while (1)
15     {
16         if (ans==36) {printf("1"); break;}
17         if (ans==0) {printf("0"); break;}
18         if (x==1) {printf("%d/36",ans); break;}
19         printf("%d/%d",ans/x,36/x);
20         break;
21     }
22     return 0;
23 }

 【E. Imprecise Computer】

题目问给出的数列能不能成为一个“差别序列”D
即问我们能不能构造出一种电脑给出的双循环赛的结果,使得其计算出的“差别序列”D恰好是给定数列
那么我们就照着给出的数列来进行构造

首先,对于差超过1的数的比较,结果是确定的,无法改变
只有相邻的数的比较结果才是可以构造的
即,对于数x来说(1<x<n),只有x和x-1,x和x+1这两次的结果是可供我们构造的
每一轮,每个数会和其他n-1个数进行比较,有n-1次赢的机会
由上可知,其中n-3次的输赢是一定的(特别的,对于1和n来说,n-2次输赢是一定的)
所以在计算d(k)=|r(1,k)-r(2,k)|时,这部分的输赢会抵消,我们无需处理
只需构造每个数和它相邻的数比较时的输赢

想到这里其实已经可以做出来了:
线性扫一遍给出的D序列,依次枚举每个数和它相邻的数电脑两轮可能给出的结果(四种)
再根据给出D序列中相应的值判断是否可以继续构造还是已经无解
下面是我的思路,也是对如何构造、如何判断的细节的考虑(实在是写的太绕了,前面已经看懂了建议自己想)

对于x和x+1之间的两轮比较:
('>'表示电脑给出的结果是x>x+1)
(r(x)=(a,b)表示:r(1,x)=a,r(2,x)=b)

比较结果 (>,>) (>,<) (<,>) (<,<)
对r(x)的相应贡献 (1,1) (1,0) (0,1) (0,0)
对r(x+1)的相应贡献 (0,0) (0,1) (1,0) (1,1)
对d(x)的相应贡献d'(x) 0 1 1 0
对d(x+1)的相应贡献d'(x+1) 0 1 1 0

                 表格E-1
显然,我们可以构造出d'(x)=d'(x+1)=0或1

所以,对于数k来说(1<k<n)
k-1和k的比较可能会对d(k)有0~1的贡献
k和k+1的比较可能会对d(k)有0~1的贡献
d(k)=d'(k)(k-1和k比较)+d'(k)(k和k+1比较)
所以得到一个结论:如果不满足
1、0<=d(k)<=2,1<k<n
2、0<=d(k)<=1,k=1或n
则必无解(构造不出,输出'NO')

n很大,我们考虑线性的做法,从1到n依次构造
定义题目输入的数列为{a(n)}
由于1很特殊,只需考虑1和2之间的比较,所以我们根据a(1)的值就可以判断构造方法,具体如下:
d(1)=d'(1)=a(1)=0时,我们构造1和2比较结果为(>,>)或(<,<),对d(2)贡献d'(2)=0
d(1)=d'(1)=a(1)=1时,我们构造1和2比较结果为(>,<)或(<,>),对d(2)贡献d'(2)=1
d(1)=d'(1)=a(1)>1时,无解

再构造2~n-1中的数k
构造这些数时,构造的前一个数会对这个数的d有一定贡献d'
比如我们此时要构造2,而上述构造1的过程中已经对d(2)有贡献,我们称为初始贡献num
即,构造k时,我们要比较k和k+1,而num=d'(k)(k-1和k比较)
我们构造的方法大致和上述构造1的方法一样
区别在于:
d(1)=d'(1)=a(1)
d(k)=num+d'(k)=a(k)

所以我们对num进行分类讨论:
(1)num=0时,相当于之前的比较对现在的构造没有贡献
那就无视之前的构造,将现在的k当做第1个来构造,即重复上述对1的构造方法
(2)num=1时
可知我们之前的构造是(>,<)或(<,>)
导致我们现在构造的时候,r(k)的初始值是(0,1)或(1,0)
(当然具体是构造了前者还是后者无所谓,因为由于之后算d(k)时取绝对值,两轮是等价的)
不妨取r(k)的值为(0,1)
我们现在构造k和k+1的比较结果,参照表格E-1得下表:

比较结果 (>,>) (>,<) (<,>) (<,<)
对r(k)的相应贡献 (1,1) (1,0) (0,1) (0,0)
最终的r(k) (1,2) (1,1) (0,2) (0,1)
最终的d(k) 1 0 2 1
对d(k+1)的相应贡献d'(k+1) 0 1 1 0

                   表格E-2
我们就根据a(k)的值进行相应构造,并更新num=d'(k+1)以构造下一个数

最终,和1一样,n也是一个特殊的数:
d(n)=d'(n)=a(n)
而构造n-1时我们得到的num即为d'(n)
最后验证一下a(n)是否符合条件即可

思路严谨地书面表达出来之后比较绕,建议在纸上动笔打草稿以更快理解

 1 #include<cstdio>
 2 using namespace std;
 3 
 4 const int N=1000100;
 5 int n,a[N],flag,num;
 6 
 7 int main()
 8 {
 9     scanf("%d",&n);
10     for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
11     for (int i=1; i<n; ++i)
12     {
13         if (a[i]>2) {flag=1; break;}    //对得到的结论的应用 
14         if (!num)                        //对1的构造并入num=0的情况 
15         {
16             if (a[i]==0) num=0;
17             if (a[i]==1) num=1;
18             if (a[i]==2) {flag=1; break;}
19         }
20         else                            //程序的主体实际只是一个分类讨论 
21         {
22             if (a[i]==0) num=1;
23             if (a[i]==1) num=0;
24             if (a[i]==2) num=1;
25         }
26     }
27     if (num!=a[n]) flag=1;                //特判一下a(n)是否符合 
28     if (a[n]>2) flag=1;
29     if (flag) printf("NO"); else printf("YES");
30     return 0;
31 }

 【G. Mobile Robot】

设给定的初始数列是{ai},我们要将机器人移动到{bi}
此时ans=max{|ai-bi|},i=1,2,...,n

令ci=ai-bi,i=1,2,...,n,则ans=max{|ci|}
由于d=|bi-b(i-1)|是固定的,那么{bi}中任意两项的差值都是固定的
所以相当于我们更改了b1的值,整个数列{bi}都会跟着改变,ci也会跟着改变
(将b1+=k(不妨k>0)时,想象一下整个{bi}都移动了k,{ci}都减小了k)
由于ans=max{|ci|},设{ci}中最大值为c_max,最小值为c_min(整个{ci}的值的更改是同步的,所以最大最小的两项也是固定的)
其实ans=max{|c_max|,|c_min|},而c_max-c_min的值是固定的
显然c_max=-c_min时最优,此时|c_max|=|c_min|

所以我们先随便写一个{bi},算出{ci},找到c_max和c_min这两项
算出c_max=-c_min成立时ans=max{|c_max|,|c_min|}=|c_max|=|c_min|

注意:
1、因为d=|bi-b(i-1)|的计算是带绝对值的,所以要分{bi}单调增和单调减两种情况讨论
2、数实在太大了,输出实数时精度不够用,反正最后输出的一位小数不是.0就是.5,就干脆手动输出实数了

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #define LL long long
 5 using namespace std;
 6 
 7 const int N=1000100;
 8 LL n,d,a[N],num,minn,maxx,pos,ans1,ans2;
 9 
10 int main()
11 {
12     scanf("%lld%lld",&n,&d);
13     for (int i=1; i<=n; ++i) scanf("%lld",&a[i]);
14     
15     num=a[1]; minn=0; maxx=0;        //就随便b1=a1算出一个{bi} 
16     for (int i=2; i<=n; ++i)
17     {
18         num+=d;                        //num相当于就是bi 
19         minn=min(minn,a[i]-num);    //找c_min 
20         maxx=max(maxx,a[i]-num);    //找c_max 
21     }
22     ans1=maxx-minn;
23     
24     num=a[1]; minn=0; maxx=0;
25     for (int i=2; i<=n; ++i)
26     {
27         num-=d;                        //{bi}单调减的情况再做一遍 
28         minn=min(minn,a[i]-num);
29         maxx=max(maxx,a[i]-num);
30     }
31     ans2=maxx-minn;
32     
33     if (ans2<ans1) ans1=ans2;        //挑小的一个 
34     if (ans1%2) printf("%lld.5",ans1/2); else printf("%lld.0",ans1/2);
35     return 0;
36 }

 

posted @ 2021-01-14 00:01  颜鸰  阅读(158)  评论(0)    收藏  举报