NOIP模拟试题2021/11/15

前言

居然说T1是签到题,直接不会,T2构造简单多了。
太蒟蒻了。qwq
被同桌吊打中。qwq

T2

容易想到一种美丽的构造方法。
n=2时:
0 1
1 0
n=4时:
1 1 0 1
0 0 0 1
1 0 0 0
1 0 1 1
n=6时:
1 1 0 1 0 1
0 0 0 1 0 1
1 1 0 0 0 0
0 0 0 0 1 1
1 0 1 0 0 0
1 0 1 0 1 1
n=8时:
1 1 0 1 0 1 0 1
0 0 0 1 0 1 0 1
1 1 0 0 0 0 0 0
0 0 0 1 0 0 1 1
1 1 0 0 1 0 0 0
0 0 0 0 0 0 1 1
1 0 1 0 1 0 0 0
1 0 1 0 1 0 1 1
容易发现就是从外围用1 1绕一圈,然后再在里面的圈继续相同的方法绕。
对于每一圈,其1的个数为:
( n 2 − 1 ) × 4 × 2 = 4 n − 8 (\frac{n}{2}-1) \times 4\times 2 =4n-8 (2n1)×4×2=4n8
特别地,对于n=2的情况,1的个数为2。

对于放置1,就变得非常简单了,外围按照这种方法放即可,里面如果能放下,即n-6后大于等于2,由于是相同的,直接递归即可。
特别地,内围n=2时,放法为
1 0
0 1

我们统计1的个数就有2种方法了
可以每次递归时计算,但对于1e9的数据非常不友好,直接爆栈。
那么可以直接先算出了,用for循环,O(n/6)得到,或者推式子O(1)得到。
这里我使用的for循环。
由于输出较大,建议快输,putchar。
代码实现:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register

#define num ch-'0'
void get(int &res){
    
    
    char ch;bool flag=0;
    while(!isdigit(ch=getchar()))
        (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getchar());res=res*10+num);
    	(flag)&&(res=-res);
}

void print(int X)
{
    
    
    if(X<0) {
    
    X=~(X-1); putchar('-');}
    if(X>9) print(X/10);
    putchar(X%10+'0');
}

int n,cas;
int ans;
bool a[10005][10005];
struct node{
    
    
	int x,y;
};

void solve(node st,node ed){
    
    
	if(n>2){
    
    
		for(re int i=st.x+3;i<=ed.x;i+=2) a[i][st.y]=a[i][st.y+1]=1;
		for(re int i=st.x;i<=ed.x-3;i+=2) a[i][ed.y]=a[i][ed.y-1]=1;
		for(re int i=st.y+3;i<=ed.y;i+=2) a[ed.x][i]=a[ed.x-1][i]=1;
		for(re int i=st.y;i<=ed.y-3;i+=2) a[st.x][i]=a[st.x+1][i]=1;
		st.x+=3;st.y+=3;
		ed.x-=3;ed.y-=3;
		n-=6;
		solve(st,ed);
	}
	else if(n==2)
		a[st.x][st.y]=a[ed.x][ed.y]=1;
}

signed main(){
    
    
	//freopen("s.in","r",stdin);
	//freopen("s.out","w",stdout);
	get(n);get(cas);
	int N=n;
	for(re int i=n;i>=2;i-=6){
    
    
		ans+=4*1ll*i-8;
		if(i==2) {
    
    ans+=2;break;}
	}
	print(ans);putchar('\n');
	if(cas){
    
    
		if(n==0){
    
    
			cout<<1<<"\n";
			cout<<1;
			return 0;
		}
		if(n==2){
    
    
			cout<<2<<"\n";
			cout<<"1 0"<<"\n";
			cout<<"0 1"<<"\n";
			return 0;
		}
		else solve((node){
    
    1,1},(node){
    
    n,n});
		for(re int i=1;i<=N;++i){
    
    
			for(re int j=1;j<=N;++j)
				putchar(a[i][j]+'0'),putchar(' ');
			putchar('\n');
		}
	}
	return 0;	
}
	

T1

对于异或,显然有如果 A ⊗ B = C A\otimes B=C AB=C,其中 A , C A,C A,C为定值后, B B B的值也唯一确定了。
证明:
令 A ⊗ B = C , A ⊗ D = C ∴ A ⊗ B = A ⊗ D 令A\otimes B=C,A\otimes D=C \\\therefore A\otimes B=A\otimes D AB=C,AD=CAB=AD
根据结合律,有
A ⊗ ( B − D ) = 0 ∴ B = D A\otimes (B-D)=0 \\\therefore B=D A(BD)=0B=D

  • 所以只要最后求的x和a[i]确定了,那么b[i]也就确定了。
    由于数据较小,可以暴力枚举每一个 a [ i ] ⊗ b [ j ] a[i]\otimes b[j] a[i]b[j]用hash统计一下相同的值的个数。
    这里hash的实现用的unorder_map,和map用法差不多,不过map内部是红黑树,是有序的,unorder顾名思义是无序的。
  • 枚举完后直接遍历unorder_map即可,如果某个对应的数量大于等于n,说明该情况可能存在,进一步判断是否存在。
    这里遍历使用的是for(auto i:mp) if(i.second>=n) check(i.first);
    意思很简单,表示用i去遍历mp这个unorder_map,其中auto表示得到这个i的变量类型。
  • 为什么是可能呢?不妨假设 A ⊗ B = x A\otimes B=x AB=x,a数组为A,C,C,C;b数组为B,B,B,B。
    我们发现枚举的时候会让 A ⊗ B A\otimes B AB的键值变成4,会通过判断,但实际是不行的,那么我们就要判断是否重复。
  • 根据上面有2个值固定了,那么另外的那个值也是固定的。
    所以我们可以求不同的b[i]值的个数,再枚举每一个a[i],找 a [ i ] ⊗ x = b [ j ] a[i]\otimes x=b[j] a[i]x=b[j]对应的键值,如果b[j]对应的键值为0,说明在上面的暴力枚举中没有一一匹配,结束。否则,减去1,表示有1个匹配上了这个值。
    还是使用unorder_map来统计个数。
    代码实现:
void check(int x){
    
    
	unordered_map<int,int>p;
	for(int i=1;i<=n;i++) p[b[i]]++;
	for(int i=1;i<=n;i++){
    
    
		if(p[a[i]^x]==0) return;
		p[a[i]^x]--;
	}
	ans[++cnt]=x;
}

最后如果结束循环后没有强制return,说明一一匹配了,统计答案。
完整代码:

#include<bits/stdc++.h>
using namespace std;

const int N=2e3+5;
int n,a[N],b[N];
unordered_map<int,int>mp;
int ans[N],cnt;

void check(int x){
    
    
	unordered_map<int,int>p;
	for(int i=1;i<=n;i++) p[b[i]]++;
	for(int i=1;i<=n;i++){
    
    
		if(p[a[i]^x]==0) return;
		p[a[i]^x]--;
	}
	ans[++cnt]=x;
}

int main(){
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			mp[a[i]^b[j]]++;
	for(auto i:mp) if(i.second>=n) check(i.first);
	cout<<cnt<<endl;
	sort(ans+1,ans+cnt+1);
	for(int i=1;i<=cnt;i++) cout<<ans[i]<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/pigonered/article/details/121338520