Goodbye 2019

Goodbye 2019

  2019年过完了,以下是我的年终总结。

  不不不,这其实是CF那场比赛的题解...

  构造,构造,交互,真真真真真是做不动;

A

  这道题比较简单,可以发现每张牌可以用多次,那么持有最大牌的人为什么不每次都出它呢?也就是说,持有最大牌的人必胜。

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define R register int
 4 
 5 using namespace std;
 6 
 7 const int N=200;
 8 int n,k1,k2,x;
 9 int a[N],b[N];
10 
11 void solve()
12 {
13     scanf("%d%d%d",&n,&k1,&k2);
14     int m1=0,m2=0;
15     for (R i=1;i<=k1;++i) scanf("%d",&x),m1=max(m1,x);
16     for (R i=1;i<=k2;++i) scanf("%d",&x),m2=max(m2,x);
17     if(m1>m2) puts("YES"); else puts("NO");
18 }
19 
20 int main()
21 {
22     int T;
23     scanf("%d",&T);
24     while(T--) solve();
25     return 0;
26 }
CF1270A

 

B

  这道题有个简单的做法:看看每一对相邻的数,看看是否有差 $\geq 2$的;为什么呢?首先,如果存在这样的数对,那这两个数就是一组合法方案;其次,一个合法方案里必然包含这样的数对;

  但是我没有想到这么美妙的做法,而是写了一个稍微复杂的。因为我们需要 $\max-\min\geq r-l+1$;也就是说如果 $\max,\min$ 固定,那么长度越短越可能满足。所以,如果一个区间的端点不是极值,那去掉也无妨。先假设左边最大右边最小,则有$\max+l\geq\min+r+1$,所以从左往右扫,中间记录 $i+a{i}$ 的最大值;再假设左边最小右边最大,则有$\max-r\geq\min-l+1$,同样从左往右扫,记录 $a_i-i+1$ 的最小值;

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define R register int
 4 
 5 using namespace std;
 6 
 7 const int N=200005;
 8 int n;
 9 int a[N];
10 
11 void solve()
12 {
13     int m=0,pos=0;
14     scanf("%d",&n);
15     for (R i=1;i<=n;++i) scanf("%d",&a[i]);
16     for (R i=1;i<=n;++i)
17     {
18         if(a[i]+i>=m)
19             pos=i,m=a[i]+i;
20         if(m>=a[i]+i+1)
21         {
22             puts("YES");
23             printf("%d %d\n",pos,i);
24             return;
25         }
26     }
27     m=2e9; pos=0;
28     for (R i=1;i<=n;++i)
29     {
30         if(a[i]-i+1<=m)
31             pos=i,m=a[i]-i+1;
32         if(m<=a[i]-i)
33         {
34             puts("YES");
35             printf("%d %d\n",pos,i);
36             return;
37         }
38     }
39     puts("NO");
40 }
41 
42 int main()
43 {
44     int t;
45     scanf("%d",&t);
46     while(t--) solve();
47     return 0;
48 }
CF1270B

 

C

  我一开始见到这道题的时候,只想到一个普适性很低的做法,仅适用于和小于两倍异或和且和为偶数的情况,也就是加入两个 $\frac{2S_{xor}-S}{2}$ ,但是很多时候并不是这种情况,那怎么办呢?题解给出了一种妙妙的解决方案,就是利用 $b_i$ 可以很大的优势,强行把 $S_{xor}$ 变很大,即加入 $2^{50}+(S%2)$,之后就按照我说的做法做就可以了;

  还有一种方法,更妙一点,就是加入两个数,$S_{xor},S_{xor}+S$,正确性显然;

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define R register int
 4 # define ll long long
 5 
 6 using namespace std;
 7 
 8 const int N=100005;
 9 int n,cnt;
10 ll a[N],b[N];
11 
12 void write()
13 {
14     printf("%d\n",cnt);
15     for (R i=1;i<=cnt;++i)
16         printf("%lld ",b[i]);
17     printf("\n");
18 }
19 
20 void solve()
21 {
22     ll s=0,sx=0; cnt=0;
23     scanf("%d",&n);
24     for (R i=1;i<=n;++i)
25         scanf("%lld",&a[i]),s+=a[i],sx^=a[i];
26     if(s==2*sx) return;
27     b[++cnt]=sx;
28     b[++cnt]=s+sx;
29 }
30 
31 
32 int main()
33 {
34     int t;
35     scanf("%d",&t);
36     while(t--)
37     {
38         solve();
39         write();
40     }
41     return 0;
42 }
CF1270C

 

D

  快乐交互,快乐自闭~

  我一开始胡了一个做法:先询问前k个,把得到的第m大扔出去,再加一个新的进来做询问,这样可以得到 $n-k+1$ 个数字;最后一次询问则是从这些数中选出k个来做询问,因为数字已知,就可以直接得到m了;然而...有可能出现 $n-k+1<k$,这个做法就没了;

  题解给出的做法十分妙妙:对于前k+1个数,枚举i,每次询问 $x\in[1,k+1],x\neq i$ 这k个数。这样有什么好处呢?如果去掉的是这些数中前m小的,那么返回的就是第m+1大,否则返回的都是前m大;也就是说,只有两种返回结果,而较大的那个返回的次数恰好就是m次,输出即可;

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define R register int
 4 
 5 using namespace std;
 6 
 7 const int N=1000;
 8 int n,k,pos,v,ans,m;
 9 int cnt[N];
10 
11 int main()
12 {
13     scanf("%d%d",&n,&k);
14     for (R i=1;i<=k+1;++i)
15     {
16         printf("? ");
17         for (R j=1;j<=k+1;++j)
18             if(i!=j) printf("%d ",j);
19         printf("\n");
20         fflush(stdout);
21         scanf("%d%d",&pos,&v);
22         if(v>=ans) m=pos,ans=v;
23         cnt[pos]++;
24     }
25     printf("! %d\n",cnt[m]);
26     fflush(stdout);
27 }
CF1270D

 

E

  不 会 了 。 以 后 再 补 吧 !

F

G

H

I

 

posted @ 2020-01-14 10:19  shzr  阅读(123)  评论(0编辑  收藏  举报