POJ 3349&&3274&&2151&&1840&&2002&&2503

(今天兴致大发学了Markdown,第一篇博客)

这次的主要都是hash的题目(当然这就意味这可以用map)

hash的方式也有很多:

  • 普通hash

  • hash挂链

  • 双hash以及自然溢出等

当然我还是喜欢挂链的(主要是精准)

下面开始看题目

3349

题意很简单,给出一片雪花的信息(六个角)

如果两片雪花相同则它们从某一点开始顺时针或逆时针数字相同

这...... 直接hash跑一边即可

主意挂链,要不然很容易WA

CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int mod=2333333,N=100005;
struct edge
{
	int to,next;
}link[N];
int head[mod],sum,n,a[N][10],k;
inline char tc(void)
{
	static char fl[100000],*A=fl,*B=fl;
	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
	x=0; char ch=tc();
	while (ch<'0'||ch>'9') ch=tc();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void add(int x,int y)
{
	link[++k].to=y; link[k].next=head[x]; head[x]=k;
}
inline bool check(int x,int y)
{
	for (register int i=1;i<=6;++i)
	{
		int p1=i,p2=1;
		while (p2<=6)
		{
			if (a[x][p1]!=a[y][p2]) break;
			if (++p1>6) p1-=6; ++p2;
		}
		if (p2>6) return 1;
	}
	for (register int i=1;i<=6;++i)
	{
		int p1=i,p2=1;
		while (p2<=6)
		{
			if (a[x][p1]!=a[y][p2]) break;
			if (--p1<1) p1+=6; ++p2;
		}
		if (p2>6) return 1;
	}
	return 0;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	register int i,j;
	memset(link,-1,sizeof(link));
	memset(head,-1,sizeof(head));
	read(n);
	for (i=1;i<=n;++i,sum=0)
	{
		for (j=1;j<=6;++j)
		read(a[i][j]),sum+=a[i][j];
		int k=sum%mod; 
		for (j=head[k];j!=-1;j=link[j].next)
		if (check(i,link[j].to)) { puts("Twin snowflakes found."); return 0; }
		add(k,i);
	}
	puts("No two snowflakes are alike.");
	return 0;
}

3274

题目大意是有n头奶牛,每个奶牛有k个特征值(用2进制下的每一位上的数来表示)

让你求一段最长的区间,使得其中奶牛的所有特征值的和分别相等

这里用sum[i][j]来表示前i头奶牛中第j个特征值的和

所以我们发现题目要求是找最长的区间l,r满足

sum[r][1]-sum[l-1][1]=sum[r][2]-sum[l-1][2]=sum[r][3]-sum[l-1][3]=...=sum[r][k]-sum[l-1][k]

对上面的式子分析后移项得:

  • sum[r][1]-sum[r][1]=sum[l-1][1]-sum[l-1][1]
  • sum[r][2]-sum[r][1]=sum[l-1][2]-sum[l-1][1]
  • sum[r][3]-sum[r][1]=sum[l-1][3]-sum[l-1][1]
  • ...
  • sum[r][k]-sum[r][1]=sum[l-1][k]-sum[l-1][1]

所以我们将sum[i]j的每一项都减去sum[i][1]

然后对于每一个i,找一下是否有和它相同的sum[i]序列

hash即可

CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int N=100005,K=35,seed=2333,mod=2333333;
struct edge
{
	int to,next;
}link[N];
int n,k,head[mod],sum[N][K],sign[N][K],x,ans,tot;
inline char tc(void)
{
	static char fl[100000],*A=fl,*B=fl;
	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
	x=0; char ch=tc();
	while (ch<'0'||ch>'9') ch=tc();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline int hash(int x)
{
	int res=1,t;
	for (register int i=1;i<=k;++i)
	{
		if ((t=sign[x][i])<0) t=-sign[x][i]*2;
		res=((long long)t*seed+res)%mod;
	}
	return res;
}
inline int max(int a,int b)
{
	return a>b?a:b;
}
inline void add(int x,int y)
{
	link[++tot].to=y; link[tot].next=head[x]; head[x]=tot;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	register int i,j,p;
	read(n); read(k);
	memset(link,-1,sizeof(link));
	memset(head,-1,sizeof(head));
	for (i=1;i<=n;++i)
	{
		read(x);
		for (j=1;j<=k;++j,x>>=1)
		if (x&1) sum[i][j]=sum[i-1][j]+1; else sum[i][j]=sum[i-1][j];
	}
	for (i=1;i<=n;++i)
	for (j=1;j<=k;++j)
	sign[i][j]=sum[i][j]-sum[i][1];
	for (i=0;i<=n;++i)
	{
		int now=hash(i);
		for (j=head[now];j!=-1;j=link[j].next)
		{
			bool flag=1;
			for (p=1;p<=k;++p)
			if (sign[link[j].to][p]^sign[i][p]) { flag=0; break; }
			if (flag) ans=max(ans,i-link[j].to);
		}
		add(now,i);
	}
	printf("%d",ans);
	return 0;
}

2151

这道题来错了地方

它其实是一道概率DP的题目

因此这里不进行讨论

题解在这里

1840

题意大概是让你求五元三次方程a1x13+a2x23+a3x33+a4x43+a5x53=0在[-50,50]范围内整数解集的个数

给出ai(1<=i<=5)

这道题最先想到的肯定就是枚举了,然而T到死(100^5)

所以我们还是移项,将原式变为:

-a1x13-a2x23=a3x33+a4x43+a5x53

然后对于-a1x13-a2x23进行hash后再枚举x3,x4,x5

复杂度陡然降低至O(100^3+100^2)

CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int mod=2333333;
struct edge
{
	int to,next;
}link[10005];
int head[mod],a[10],ans,tot;
inline int hash(int x)
{
	if (x<0) x=-2*x;
	return x%mod;
}
inline void add(int x,int y)
{
	link[++tot].to=y; link[tot].next=head[x]; head[x]=tot;
}
int main()
{
	memset(link,-1,sizeof(link));
	memset(head,-1,sizeof(head));
	register int i,j,k,p;
	for (i=1;i<=5;++i)
	scanf("%d",&a[i]);
	for (i=-50;i<=50;++i)
	for (j=-50;j<=50;++j)
	{
		if (!i||!j) continue;
		int t=a[1]*i*i*i+a[2]*j*j*j;
		add(hash(-t),t);
	}
	for (i=-50;i<=50;++i)
	for (j=-50;j<=50;++j)
	for (k=-50;k<=50;++k)
	{
		if (!i||!j||!k) continue;
		int t=a[3]*i*i*i+a[4]*j*j*j+a[5]*k*k*k,h=hash(t);
		for (p=head[h];p!=-1;p=link[p].next)
		{
			int temp=t+link[p].to;
			if (!temp) ++ans;
		}
	}
	printf("%d",ans);
	return 0;
}

2002

题意很简单,在平面直角坐标系的整点之间找出正方形的个数

这是一个经典的问题,一般都是用O(n^2)的算法,即枚举两个端点然后hash判断出另外两个点是否存在

这里要用到已知正方形两个顶点坐标找正方形另外两个顶点坐标的公式(画画图证证三角形全等即可):

如果A(x1, y1), B(x2, y2) ,则C(x3, y3),D(x4, y4) 为:

x3=x1+(y1-y2);y3=y1-(x1-x2); (在直线AB上方)

x4=x2+(y1-y2);y4=y2-(x1-x2)

或:

x3=x1-(y1-y2);y3=y1+(x1-x2); (在直线AB下方)

x4=x2-(y1-y2);y4=y2+(x1-x2)

最后ans除个8即可(四条边,两个顶点重复枚举)

CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005,MAX=80005;
struct node
{
	int x,y;
}a[N];
struct edge
{
	int to,next;
}link[N];
int n,head[MAX],k,ans;
inline char tc(void)
{
	static char fl[100000],*A=fl,*B=fl;
	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
	x=0; char ch=tc(); int flag=1;
	while (ch<'0'||ch>'9') { if (ch=='-') flag=-1; ch=tc(); }
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
	x*=flag;
}
inline void write(int x)
{
	if (x/10) write(x/10);
	putchar(x%10+'0');
}
inline int hash(int x,int y)
{
	if (x<0) x=-2*x; 
	if (y<0) y=-2*y;
	return x+y;
}
inline void add(int x,int y)
{
	link[++k].to=y; link[k].next=head[x]; head[x]=k;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	register int i,j,p;
	for (;;)
	{
		read(n); k=ans=0;
		if (!n) break;
		memset(link,-1,sizeof(link));
		memset(head,-1,sizeof(head));
		for (i=1;i<=n;++i)
		{
			read(a[i].x); read(a[i].y);
			add(hash(a[i].x,a[i].y),i);
		}
		for (i=1;i<=n;++i)
		for (j=1;j<=n;++j)
		{
			if (i==j) continue;
			int x=a[i].x-a[j].x,y=a[i].y-a[j].y,flag=0;
			int now1=hash(a[i].x-y,a[i].y+x),now2=hash(a[j].x-y,a[j].y+x);
			for (p=head[now1];p!=-1;p=link[p].next)
			if (a[link[p].to].x==a[i].x-y&&a[link[p].to].y==a[i].y+x) { ++flag; break; }
			for (p=head[now2];p!=-1;p=link[p].next)
			if (a[link[p].to].x==a[j].x-y&&a[link[p].to].y==a[j].y+x) { ++flag; break; }
			if (flag==2) ++ans; flag=0;
			int now3=hash(a[i].x+y,a[i].y-x),now4=hash(a[j].x+y,a[j].y-x);
			for (p=head[now3];p!=-1;p=link[p].next)
			if (a[link[p].to].x==a[i].x+y&&a[link[p].to].y==a[i].y-x) { ++flag; break; }
			for (p=head[now4];p!=-1;p=link[p].next)
			if (a[link[p].to].x==a[j].x+y&&a[link[p].to].y==a[j].y-x) { ++flag; break; }
			if (flag==2) ++ans; flag=0;
		}
		write(ans/8); putchar('
');
	}
	return 0;
}

2503

垃圾字符串散列问题

读入巨坑,以我的字符串处理技术果不其然的WA了(应该是挂在了读入上)

然后改用字符数组,判断两字符串是否相等只能使用按位比较的方法,果不其然的T了

就想骂人了,突然记起来字符数组也可以扔在map里用(神奇),最后map水过

不过这种类型的问题也可以用字典树来解决,等以后会了再来填坑

CODE

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<map>
using namespace std;
map <string,string> hash;
const int N=100005;
char key[N],s[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	while ((key[0]=getchar())!='
')
	{
		scanf("%s%s",key+1,s);
		hash[s]=key; getchar();
	}
	while (scanf("%s",&s)!=EOF)
	if (hash.count(s)) cout<<hash[s]<<endl; else puts("eh");
	return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/8727739.html