<button class="accordion">吐槽

分类讨论 + 构造大赛(雾

B分类讨论的时候不小心把 0 打成 1 FST,D初值设小不然 1h 就过了,直接把上分场整成了掉分场,枯了。

</button>

A - XORwice

题目大意

给你 \(a\)\(b\),求 \((a \oplus x) + (b \oplus x)\) 的最小值。

解题思路

考虑怎么构造 \(x\) 才能使要求的值最小。

对于每一位,如果 \(a,b\) 都是 \(0\) ,那么当 \(x\) 的这一位为 \(0\) 时,异或后的这一位一定是零,肯定最优。

如果 \(a,b\) 一个是 \(1\),一个是 \(0\) ,那么当 \(x\) 的这一位不论是 \(0\) 还是 \(1\) ,异或后一定是一 \(0\)\(1\) ,不妨就认为 \(x\) 的这一位是 \(0\)

如果 \(a,b\) 都是 \(1\) ,那么当 \(x\) 的这一位为 \(1\) 时,异或后的这一位一定是零,肯定最优。

简单观察就能发现,上面的构造其实就是 \(a \land b\)

所以答案就是 \((a \oplus (a \land b)) + (b \oplus (a \land b))\)

Code

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define int long long

using namespace std;

int read()
{
	int ans=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
	return ans*f;
}

int t,a,b;

signed main()
{
	t=read();
	while(t--)
	{
		a=read();b=read();
		printf("%lld\n",(a^(a&b))+(b^(a&b)));
	}
	return 0;
}

B - Putting Bricks in the Wall

题目大意

给定一个 \(01\) 矩阵,一个人会从 \((1,1)\) 走到 \((n,n)\),他途中会只走 \(0\) 或直走 \(1\)

现在可以将做多两个格子 \(01\) 反转,使得这个人无法找到一条可行的路径

解题思路

显然如果 \((2,1),(1,2)\) 两个格子的数字与 \((n,n-1),(n-1,n)\) 的数字不同,就一定没有可行路径,因为路径必定经过这四个格子的其中两个。

所以分类讨论一下就好了,显然可以在两次反转内做到这件事。

Code

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define int long long

using namespace std;

int read()
{
	int ans=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
	return ans*f;
}

const int N=205;
int t,n;
char mmap[N][N];

signed main()
{
	t=read();
	while(t--)
	{
		n=read();
		for(int i=1;i<=n;++i)
			scanf("%s",mmap[i]+1);
		if(mmap[1][2]=='0'&&mmap[2][1]=='0')
		{
			if(mmap[n][n-1]=='1'&&mmap[n-1][n]=='1')
				printf("0\n");
			else if(mmap[n][n-1]=='1')
				printf("1\n%lld %lld\n",n-1,n);
			else if(mmap[n-1][n]=='1')
				printf("1\n%lld %lld\n",n,n-1);
			else printf("2\n%lld %lld\n%lld %lld\n",n,n-1,n-1,n);
		}
		else if(mmap[1][2]=='0'&&mmap[2][1]=='1')
		{
			if(mmap[n][n-1]=='1'&&mmap[n-1][n]=='1')
				printf("1\n%lld %lld\n",2,1);
			else if(mmap[n][n-1]=='1')
				printf("2\n%lld %lld\n%lld %lld\n",2,1,n-1,n);
			else if(mmap[n-1][n]=='1')
				printf("2\n%lld %lld\n%lld %lld\n",2,1,n,n-1);
			else printf("1\n%lld %lld\n",1,2);
		}
		else if(mmap[1][2]=='1'&&mmap[2][1]=='0')
		{
			if(mmap[n][n-1]=='1'&&mmap[n-1][n]=='1')
				printf("1\n%lld %lld\n",1,2);
			else if(mmap[n][n-1]=='1')
				printf("2\n%lld %lld\n%lld %lld\n",1,2,n-1,n);
			else if(mmap[n-1][n]=='1')
				printf("2\n%lld %lld\n%lld %lld\n",1,2,n,n-1);
			else printf("1\n%lld %lld\n",2,1);
		}
		else
		{
			if(mmap[n][n-1]=='0'&&mmap[n-1][n]=='0')
				printf("0\n");
			else if(mmap[n][n-1]=='0')
				printf("1\n%lld %lld\n",n-1,n);
			else if(mmap[n-1][n]=='0')
				printf("1\n%lld %lld\n",n,n-1);
			else printf("2\n%lld %lld\n%lld %lld\n",n,n-1,n-1,n);
		}
	}
	return 0;
}

C - Palindromifier

题目大意

给一个 \(n\) 个字符的字符串,每次操作可以选择将 \(2 \sim i\) 的字符反转并添加到字符串的前部或将 \(i \sim n-1\) 的字符串反转并添加到字符串的末尾。

\(30\) 次操作内使得这个字符串回文。

解题思路

个人觉得这个 \(30\) 挺误导人的。

因为可以 \(3\) 次操作就做到这件事。

  1. \(2 \sim 2\) 反转并添加到字符串前部,这样假设原来字符串是 \(XOXXX...\) ,反转后就能得到 \(OXOXXX...\)

  2. \(2 \sim n-1\) 反转并添加到字符串尾部,这样假设原来字符串是 \(OXOXXX...\),那么反转后的字符串就是 \(OXOXXX......XXXOX\),此时 \(2\sim n\) 已经回文。

  3. \(n-1 \sim n-1\) 反转并添加到字符串尾部,这样假设原来字符串是 \(OXOXX......XXOX\),那么反转后的字符串就是 \(OXOXX......XXOXO\),显然回文。

(Ps:上述中的 \(O\) 为一固定字符,\(X\) 为任意字符,且 \(n\) 是每次操作后的字符串长度而非读入的字符串长度)

Code

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define int long long

using namespace std;

int read()
{
	int ans=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
	return ans*f;
}

const int N=1e5+5;
int n;
char s[N];

signed main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	printf("3\n");
	printf("L 2\n");n++;
	printf("R 2\n");n+=n-2;
	printf("R %lld\n",n-1);
	return 0;
}

D - Hexagons

题目大意

有一个二元组 \((a,b)\) ,每次可以把这个二元组进行 \((a+1,b+1),(a,b+1),(a-1,b),(a-1,b-1),(a,b-1),(a+1,b)\) 的加减操作。

已知每种操作的代价(代价为正数),初始时的二元组为 \((0,0)\),求把二元组变成 \((x,y)\) 需要的最小代价。

解题思路

显然分类讨论,通常可以分为三种情况:

  1. \((x,y)\) 直接通过 \((a,b+1),(a-1,b),(a,b-1),(a+1,b)\) 加减到位。

  2. 先通过 \((a+1,b+1),(a-1,b-1)\) 把同时加减到 \((x,x)\),再通过第一种情况加减到 \((x,y)\)

  3. 先通过 \((a+1,b+1),(a-1,b-1)\) 把同时加减到 \((y,y)\),再通过第一种情况加减到 \((x,y)\)

这题初值要设到 \(2 \times 10^{18}\),然后我习惯性的设成了 \(10^{18}\) 就没了qwq

Code

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define int long long

using namespace std;

int read()
{
	int ans=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
	return ans*f;
}

int t,x,y,C1,C2,C3,C4,C5,C6;

signed main()
{
	t=read();
	while(t--)
	{
		x=read();y=read();
		C1=read();C2=read();C3=read();
		C4=read();C5=read();C6=read();
		int ans=1e19;
		if(x==0&&y==0) ans=0;
		else if(x<=0&&y<=0)
		{
			x=-x,y=-y;
			ans=min(ans,x*C3+y*C5);
			if(x>=y) ans=min(ans,y*C4+(x-y)*C3);
			else ans=min(ans,x*C4+(y-x)*C5);
			if(x>=y) ans=min(ans,x*C4+(x-y)*C2);
			else ans=min(ans,y*C4+(y-x)*C6);
		}
		else if(x<=0&&y>=0)
		{
			x=-x;
			ans=min(ans,x*C3+y*C2);
			ans=min(ans,x*C4+(x+y)*C2);
			ans=min(ans,y*C1+(x+y)*C3);
		}
		else if(x>=0&&y<=0)
		{
			y=-y;
			ans=min(ans,x*C6+y*C5);
			ans=min(ans,x*C1+(x+y)*C5);
			ans=min(ans,y*C4+(x+y)*C6);
		}
		else
		{
			ans=min(ans,x*C6+y*C2);
			if(x>=y) ans=min(ans,y*C1+(x-y)*C6);
			else ans=min(ans,x*C1+(y-x)*C2);
			if(x>=y) ans=min(ans,x*C1+(x-y)*C5);
			else ans=min(ans,y*C1+(y-x)*C3);
		}
		printf("%lld\n",ans);
	}
	return 0;
}