哈希的一些

理解就是,对于一些给定的东西,进行加密然后存入数组通常的加密一般是,转进制,比如说a-z这么二十六个字母,显然你可以将其转成数字之后再乘26每次,但是可能会有所超出限制比如说3823849534952342545353这么一串数字你明显保存不下来,怎么办呢,那么mod就可以排上用场,mod一个998244353就很不错。哦记住一下要模一个质数。
大佬发言: 所以我们主要处理“Hash函数”和“冲突情况”

  1. 一般Hash函数的构造需要一个mod数,通常定义为 不大于
    n的最大素数 ,这样随机数据可以均匀的映射在构建的链表里。
  2. 同一个链中可能出现多个数,有一种“开散列”的解决案
    是,将原始值映射后值相同的归为一类,构成一个链,接在表头
    (映射值)之后,每条链的节点可以保存一些数据(我这道题就
    是利用开散列),之后依次遍历即可,因为依赖于Hash函数,构
    造的越好越接近O(1)

做一题hash:P1955 [NOI2015] 程序自动分析就是hash加并查集加一些优化,其实这个用的是hash表,具体就像链式前向星一样,用last与now来维护具体:

int in(int x)//我重点讲一下这一段,不妨考虑两种种情况:
//讨论两种,若之前有,返回之前的,若之前没有,新开一个 
//感觉有更好的写法 呃大概没有了 
{
	int v=x%mod;
	for(int i=last[v];i;i=h[i]) 
	{
		if(to[i]==x) return i;
	}
	h[++tot]=last[v],to[tot]=x,last[v]=tot;
	return tot;
}

查询就和查询操作一样,就这样:

#include<bits/stdc++.h>
using namespace std;
int n,m,tot=0,tp=0,wb=0;
int to[2000001],fa[2000001],h[2000001],last[2000001];
int a[2000001],mod=1111111;
int xx[2000001],yy[2000001];//st[100001];
int findfa(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=findfa(fa[x]);
}
int in(int x)//我重点讲一下这一段,不妨考虑两种种情况:
//讨论两种,若之前有,返回之前的,若之前没有,新开一个 
//感觉有更好的写法 呃大概没有了 
{
	int v=x%mod;
	for(int i=last[v];i;i=h[i]) 
	{
		if(to[i]==x) return i;
	}
	h[++tot]=last[v],to[tot]=x,last[v]=tot;
	return tot;
}
int asknum(int x)
{
	int v=x%mod;
	for(int i=last[v];i;i=h[i])
	{
		if(to[i]==x) return i;
	}
	return 0;
}
void clean()
{
	for(int i=1;i<=1000001;i++) fa[i]=i;
	memset(h,0,sizeof(h));memset(last,0,sizeof(last));
	tot=0;wb=0;
	return ;
}
int main()
{
	int t;scanf("%d",&t);
	while(t--)
	{
		int pd=1;clean();
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			int x,y,p;scanf("%d%d%d",&x,&y,&p);
			if(p==1) 
			{
				x=in(x);y=in(y);//printf("*%d %d\n",x,y);
				int fx=findfa(x),fy=findfa(y);
				if(fx!=fy) fa[fx]=fy;
			}
			else xx[++wb]=x,yy[wb]=y;
		}
		for(int i=1;i<=wb;i++)
		{
			if(xx[i]==yy[i]) 
			{
				printf("NO\n");
				pd=-1;break;
			}
			int x=asknum(xx[i]),y=asknum(yy[i]);//printf("*%d %d\n",x,y);
			if(x&&y&&findfa(x)==findfa(y))
			{
				printf("NO\n");
				pd=-1;break;
			}
		}
		if(pd==1) printf("YES\n");
//		for(int i=1;i<=n;i++) printf("%d ",findfa(i));
	}
	
	return 0;
}

下一个P1381 单词背诵小水题用string+map维护就行:

#include<bits/stdc++.h>
using namespace std;
map<string ,int >sum;
map<string ,bool > v;
int n,m,ans1=0,ans2=1e9;
string s[200001],sl;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) cin>>sl,v[sl]=1;
	scanf("%d",&m);int l=1;
	for(int i=1;i<=m;i++)
	{
		cin>>s[i];
		if(v[s[i]]) sum[s[i]]++;
		if(sum[s[i]]==1) ans1++,ans2=i-l+1;
		while(l<=i)
		{
			if(!v[s[l]]) 
			{
				l++;
				continue;
			}
			if(sum[s[l]]>=2) 
			{
				sum[s[l]]--;
				l++;
				continue ;
			}
			break;
		}	
		ans2=min(ans2,i-l+1);
	}
	printf("%d\n%d",ans1,ans2);
	return 0;
} 

下一个是一个可以不用hash的题目P4398 [JSOI2008]Blue Mary的战役地图所以就没做了(其实做了不过用dp QWQ),P3879 [TJOI2010] 阅读理解评蓝属实不太合理,不难看起来,呃属实是set万岁,学了一下set,用于判断set中有无这个数用count(x)有返回true,无返回false。

//学习新姿势,用set进行维护会好很多,哦最好使用双哈希 
#include<bits/stdc++.h>
using namespace std;
int n,m,mod1=9982245353,mod2=11111111;
char a[1000001];
set<int> hs1[1000001],hs2[1000001];
void gethash(char s[],int len,int &k1,int &k2)
{
	for(int i=1;i<=len;i++) k1=(k1*111+s[i])%mod1;
	for(int i=1;i<=len;i++) k2=(k2*111+s[i])%mod2;
	return ;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x,len,k1=0,k2=0;scanf("%d",&x); 
		while(x--)
		{
			scanf("%s",a+1);len=strlen(a+1);k1=0,k2=0;
			gethash(a,len,k1,k2);hs1[i].insert(k1);hs2[i].insert(k2);
		}
	}
	scanf("%d",&m);
	while(m--)
	{
		int len,k1=0,k2=0;scanf("%s",a+1);
		len=strlen(a+1);gethash(a,len,k1,k2);
		for(int i=1;i<=n;i++)
		{
			if(hs1[i].count(k1)==true&&hs2[i].count(k2)==true) printf("%d ",i);
		}
		printf("\n");
	}
	return 0;
}

来个有趣些的,P5043 【模板】树同构([BJOI2015]树的同构)看一下第一个大佬的文章:https://www.luogu.com.cn/problem/solution/P5043,我们对于树同构应该先计算出重心,可能有一两个,记录下来,求重心的方式也较为巧妙,采用多项式hash(?)的方法,具体说:指的是,算了有点麻烦,我们采用另一种呃也是不知道什么hash:(学到了,我们下次把hash打乱~)

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int ans[10001][1001];
int len=0,last[1000001];
int n,m,mod=998244353,power=11111;
struct pp
{
	int x,y,next;
};pp p[1000001]; 
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
bool cmp(const int &x,const int &y)
{
	return x<y;
}
int gethash(int x,int fa)
{
	int q[1001],ans=0,tot=0;
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		q[++tot]=gethash(y,x);
	}
	sort(q+1,q+tot+1,cmp);
	for(int i=1;i<=tot;i++) ans=(ans*power+q[i])%mod;
	return (ans*power)%mod+1101;
}
signed main()
{
	scanf("%lld",&m);
	for(int i=1;i<=m;i++)
	{
		memset(last,-1,sizeof(last));len=0;
		scanf("%lld",&n);
		for(int j=1;j<=n;j++)
		{
			int x;scanf("%lld",&x);
			if(x!=0) ins(j,x),ins(x,j);
		}
		for(int j=1;j<=n;j++) ans[i][j]=gethash(j,0);
		sort(ans[i]+1,ans[i]+n+1,cmp);
		for(int j=1;j<=i;j++)
		{
			int k=0;
			while(k<=n)
			{
				if(ans[i][++k]!=ans[j][k]) break;
			}
			if(k>n) 
			{
				printf("%lld\n",j);
				break;
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值