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 }
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 }
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 }
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 }
E
不 会 了 。 以 后 再 补 吧 !
F
G
H
I