【洛谷1745】[CERC2016] Lost Logic(构造题)
- 一个表达式形如\((!)x_i\rightarrow(!)x_j\),表示\(x_i\)为真(假)时\(x_j\)一定为真(假)。
- 给定\(n\)个\(01\)变量的三组取值,要求构造若干表达式使得有且仅有这三组解,或判断无法构造。
- \(n\le50\),表达式个数不能超过\(500\)
简单分类讨论
如果一个变量\(x_i\)在三组中取值相同(假设都为真),只要用一个表达式\(!x_i\rightarrow x_i\)即可满足条件。
否则,它必然会在某一组中取值与另两组不同,假设是在第\(t\)组。
对于\(t\)相同的两个变量\(x_i,x_j\),确定了\(x_i\)也就确定了\(x_j\),可以直接利用两个表达式描述这层关系。
因此最后只需保留\(t\)不同的变量,此时我们只能在一个变量取到它的第\(t\)组取值时才能确定其他变量的取值。
实际上,当三种变量都存在的时候是无法构造的,否则显然可行。
代码:\(O(n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
using namespace std;
int n,p[3],a[3][N+5];
namespace Printer
{
#define pc(x) (*C++=x)
#define write(x) (x>9&&pc(x/10+48),pc(x%10+48))
int tot;char FO[100000],*C=FO;I void clear() {fwrite(FO,1,C-FO,stdout);}
I void print(CI x,CI p,CI y,CI q)//x=p -> y=q
{
++tot,!p&&pc('!'),pc('x'),write(x),pc(' '),pc('-'),pc('>'),pc(' '),!q&&pc('!'),pc('x'),write(y),pc('\n');
}
}using namespace Printer;
int main()
{
RI i,j,k;for(scanf("%d",&n),k=0;k<=2;++k) for(i=1;i<=n;++i) scanf("%d",a[k]+i);
RI t;for(i=1;i<=n;++i)
{
if(a[0][i]==a[1][i]&&a[0][i]==a[2][i]) {print(i,a[0][i]^1,i,a[0][i]);continue;}//在三组中取值相同
if(!p[t=a[0][i]^a[1][i]?(a[0][i]^a[2][i]?0:1):2]) {p[t]=i;continue;}//尚无这一种变量,记录下来
print(p[t],a[t][p[t]],i,a[t][i]),print(p[t],a[t][p[t]]^1,i,a[t][i]^1);//可以直接确定
}
if(p[0]&&p[1]&&p[2]) return puts("-1"),0;//如果三种变量都存在则无解
for(i=0;i<=2;++i) for(j=0;j<=2;++j) i^j&&p[i]&&p[j]&&(print(p[i],a[i][p[i]],p[j],a[i][p[j]]),0);//只有取到对应组取值时才能确定另一类变量
return printf("%d\n",tot),clear(),0;
}
待到再迷茫时回头望,所有脚印会发出光芒