Codeforces Round 931 (Div. 2)
Preface
最近CF比赛好多,但都是Div2不太想熬夜打的说,还是抽空补补题吧
这场感觉A~D2都超级简单一眼秒,然后E做不来一点直接开摆
A. Too Min Too Max
签到,选出最小的两个数和最大的两个数即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=105;
int t,n,a[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+n+1); printf("%d\n",abs(a[1]-a[n])+abs(a[n]-a[2])+abs(a[2]-a[n-1])+abs(a[n-1]-a[1]));
}
return 0;
}
B. Yet Another Coin Problem
一个显而易见的贪心就是优先拿面额大的,但这样会出问题(比如\(12\)用\(10,1,1\)就要三次,用\(6,6\)就只用两次)
但不难发现只有小范围才会出现这种问题,具体的,由于所有面额的最小公倍数是\(30\),因此我们DP出面额\(\le 30\)的答案,然后多出的部分都贪心地取面额\(15\)的即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=35,c[5]={1,3,6,10,15};
int t,n,f[N];
inline void init(CI n)
{
f[0]=0; for (RI i=1;i<=n;++i)
{
f[i]=1e9;
for (RI j=0;j<5;++j) if (i>=c[j])
f[i]=min(f[i],f[i-c[j]]+1);
}
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t),init(30);t;--t)
{
scanf("%d",&n); if (n<=30) { printf("%d\n",f[n]); continue; }
int dlt=(n-30+14)/15; n-=15*dlt; printf("%d\n",dlt+f[n]);
}
return 0;
}
C. Find a Mine
总感觉这个题在CF上做过几乎一样的,应该就是一年内的比赛但找不到是哪场了
首先考虑询问\((1,1),(n,m)\),这样可以分别得到两个点的\(x+y\)的值\(sum_1,sum_2\)
然后不妨再问一次\((n,1)\),这样可以得到其中某个点的\(x-y\)的值\(sub\)
我们尝试用\(sum_1\)和\(sub\)还原出一个坐标\((x_1,y_1)\),然后直接询问这个坐标
如果答案为\(0\)就说明这个点就有mine,否则用\(sum_2\)和\(sub\)还原出另一个坐标\((x_2,y_2)\),则\((x_2,y_2)\)一定满足要求
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=35,c[5]={1,3,6,10,15};
int t,n,m;
inline int ask(CI x,CI y)
{
printf("? %d %d\n",x,y); fflush(stdout);
int z; scanf("%d",&z); return z;
}
inline void print(CI x,CI y)
{
printf("! %d %d\n",x,y); fflush(stdout);
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
scanf("%d%d",&n,&m); int sum1=ask(1,1)+2,sum2=n+m-ask(n,m);
auto calc=[&](CI sum,CI sub)
{
int x=(sum-sub)/2,y=(sum+sub)/2;
x=min(max(x,1),n); y=min(max(y,1),m);
return pi(x,y);
};
int sub=ask(n,1)+1-n; auto [x_1,y_1]=calc(sum1,sub);
if (ask(x_1,y_1)==0) { print(x_1,y_1); continue; }
auto [x_2,y_2]=calc(sum2,sub); print(x_2,y_2);
}
return 0;
}
D1. XOR Break — Solo Version
首先不难发现当\(n\)为\(2\)的幂次时一定无解,否则考虑找出\(n\)的最高位\(k\),分类讨论:
若\(m\)的第\(k\)位为\(1\),则此时可以一步从\(n\to m\),因为此时天然满足\(m<n\),且\(m\oplus n\)的第\(k\)位为\(0\)因此亦\(<n\)
若\(m\)的第\(k\)位为\(0\),则此时一定要先将\(n\to n'\),其中\(n'\)的第\(k\)位为\(0\),否则还是会无限面临这个局面
不妨设\(t=n\oplus 2^k\),考虑\(n'\)需要满足的性质,和上面类似,我们需要找到\(t\)的最高位\(h\),则\(n'\)的最高位必须为\(h\)且后面的位都可以任选
不难发现将\(n'\)取得尽量大一定不会更劣,因此令\(n'=2^{h+1}-1\)即可,如果此时\(m<n'\)则下一步一定可以完成\(n'\to m\),否则一定无解
因此这题有点诈骗的感觉了,其实有解的话最多也就操作两次即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n,m;
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%lld",&t);t;--t)
{
scanf("%lld%lld",&n,&m); vector <int> ans={n};
int k=__lg(n); if (n==(1LL<<k)) { puts("-1"); continue; }
if ((m>>k)&1) ans.push_back(m); else
{
int h=__lg(n^(1LL<<k));
ans.push_back(n=(1LL<<h+1)-1);
if (n<m) { puts("-1"); continue; }
if (n!=m) ans.push_back(m);
}
for (RI i=1;i<ans.size();++i)
assert(ans[i]<ans[i-1]&&(ans[i]^ans[i-1])<ans[i-1]);
printf("%lld\n",ans.size()-1);
for (auto x:ans) printf("%lld ",x); putchar('\n');
}
return 0;
}
D2. XOR Break — Game Version
这题更是比D1还简单,只要稍微写个暴力打个表就能发现极其明显的规律,并且这个规律还很好证明
先直接给出结论:当\(n\)的二进制表示中\(1\)的个数为偶数时先手必胜;否则先手必败
考虑证明,不妨先设\(n\)的二进制表示中有偶数个\(1\),则此时先手总能找到一种划分方案,使得\(p_1,p_2\)中\(1\)的个数都是奇数
具体地,我们令\(k\)为\(n\)的最高位,则\(p_1=2^k,p_2=n\oplus 2^k\)总是一种合法的划分方案
因此对手无论如何操作都会把\(n\)变成一个有奇数个\(1\)的状态,此时不管对手怎么划分,一定会分出一个有奇数个\(1\)、一个有偶数个\(1\)的状态,我们只要选择有偶数个\(1\)的那个即可
\(n\)的二进制表示中有奇数个\(1\)的情况也是类似,我们把先手让给对面然后用相同的策略即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n;
inline void solve(CI n)
{
int k=__lg(n); printf("%lld %lld\n",1LL<<k,n^(1LL<<k)); fflush(stdout);
}
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%lld",&t);t;--t)
{
if (scanf("%lld",&n),__builtin_parityll(n))
printf("second\n"),fflush(stdout); else printf("first\n"),fflush(stdout),solve(n);
for (;;)
{
int p1,p2; scanf("%lld%lld",&p1,&p2);
if (p1==0&&p2==0) break;
solve(n=__builtin_parityll(p1)?p2:p1);
}
}
return 0;
}
Postscript
接下来好像陆陆续续地要忙起来了,不知道还有没有时间来补之前欠下的题来着