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

接下来好像陆陆续续地要忙起来了,不知道还有没有时间来补之前欠下的题来着

posted @ 2024-03-06 16:29  空気力学の詩  阅读(28)  评论(0编辑  收藏  举报