HDU 6865 Kidnapper's Matching Problem 的一些作题心得

题意就不赘述。
此题中两个元素\(x,y\)相等的充分必要条件是\(x,y\)对于某个线性基等价。
等价即为从线性基中选出若干个元素异或上\(x\)后等于\(y\)
那么第一种思考就是网上的常见做法
借鉴大佬的博客
将一个等价集合变成他的代表元素,这个代表元素即为该集合中所有元素异或上线性基中对应存在位的值得到的。
那么为什么代表元素只有一个呢?
考虑反证法
假设\(c \neq d\)
考虑\(a,b \in S\) \(S\)代表等价集合。
考虑\(a \bigoplus some \ elements \ in \ L = c\)
同理\(b \bigoplus some \ elements \ in \ L = d\)
那么可推出\(c \bigoplus some \ elements \ in \ L = d\)
但是\(L\)中的元素最高位\(1\)最高的那个\(1\)一定不会被去掉,所以选的集合只能为空集。
那么\(c = d\)与假设矛盾
附上代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=30;
const int LEN=200010;
const int M=1e9+7;
int t,n,m,k,a[LEN],b[LEN],ne[LEN];
struct linearbase{
	int val[N];
	void insert(int x){
		for (int i=N-1; i>=0&&x; --i)
		if ((x>>i)&1){
			if (!val[i]){
				val[i]=x;
				break;
			}
			x^=val[i];
		}
	}
	int check(int x){
		for (int i=N-1; i>=0; --i)
		if ((x>>i)&1) x^=val[i];
		return x;
	}
	void clear(){
		for (int i=0; i<N; ++i) val[i]=0;
	}
}L;
int fp(int x,int y){
	int ret=1;
	for (; y; y>>=1,x=(ll)x*x%M) if (y&1) ret=(ll)ret*x%M;
	return ret; 
}
void getnext(){
	for (int i=2,j=0; i<=m; ++i){
		while (j&&b[i]!=b[j+1]) j=ne[j];
		if (b[i]==b[j+1]) ne[i]=++j; else ne[i]=0;//clear
	}
}
int KMP(){
	int ret=0;
	getnext();
	for (int i=1,j=0; i<=n; ++i){
		while (j&&a[i]!=b[j+1]) j=ne[j];
		if ((a[i]==b[j+1])&&(++j==m)){
			(ret+=fp(2,i-m))%=M;
			j=ne[j];
		}
	}
	return ret;
}
int main(){
	scanf("%d",&t);
	while (t--){
		L.clear();
		scanf("%d%d%d",&n,&m,&k);
		for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
		for (int i=1; i<=m; ++i) scanf("%d",&b[i]);
		for (int i=1,x; i<=k; ++i){
			scanf("%d",&x);
			L.insert(x);
		}
		for (int i=1; i<=n; ++i) a[i]=L.check(a[i]);
		for (int i=1; i<=m; ++i) b[i]=L.check(b[i]);
		cout<<KMP()<<'\n';
	}
}

第二种考虑来自石昕怡学姐,如果\(x \bigoplus y\)的值对于线性基来说与零等价,那么\(x\)等价于\(y\)
代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=30;
const int LEN=200010;
const int M=1e9+7;
int t,n,m,k,a[LEN],b[LEN],ne[LEN];
struct linearbase{
	int val[N];
	void insert(int x){
		for (int i=N-1; i>=0&&x; --i)
		if ((x>>i)&1){
			if (!val[i]){
				val[i]=x;
				break;
			}
			x^=val[i];
		}
	}
	int check(int x){
		for (int i=N-1; i>=0; --i)
		if ((x>>i)&1) x^=val[i];
		return x;
	}
	void clear(){
		for (int i=0; i<N; ++i) val[i]=0;
	}
}L;
int fp(int x,int y){
	int ret=1;
	for (; y; y>>=1,x=(ll)x*x%M) if (y&1) ret=(ll)ret*x%M;
	return ret; 
}
void getnext(){
	for (int i=2,j=0; i<=m; ++i){
		while (j&&L.check(b[i]^b[j+1])) j=ne[j];
		if (L.check(b[i]^b[j+1])==0) ne[i]=++j; else ne[i]=0;//clear
	}
}
int KMP(){
	int ret=0;
	getnext();
	for (int i=1,j=0; i<=n; ++i){
		while (j&&L.check(a[i]^b[j+1])) j=ne[j];
		if (L.check(a[i]^b[j+1])==0&&(++j==m)){
			(ret+=fp(2,i-m))%=M;
			j=ne[j];
		}
	}
	return ret;
}
int main(){
	scanf("%d",&t);
	while (t--){
		L.clear();
		scanf("%d%d%d",&n,&m,&k);
		for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
		for (int i=1; i<=m; ++i) scanf("%d",&b[i]);
		for (int i=1,x; i<=k; ++i){
			scanf("%d",&x);
			L.insert(x);
		}
		cout<<KMP()<<'\n';
	}
}
posted @ 2020-09-13 20:11  Yuhuger  阅读(167)  评论(0编辑  收藏  举报