点开一道第是自己oj的第440大关,想a了,一直想却无果,学长一句点醒,开始写hash。

关于这道题呢很无语了,两天卡在这上面,而且有些dalao不到20min就a了。我太菜了。

所以要深入讨论这道题啊,这时oj上的hash最后一题了,仔细总结!

首先我们发现求出前缀和后有一个n^2暴力枚举的做法可这道题n<=100000,很明显这是要我们搞出来一个nlog(n)的做法才行。

考虑优化,首先我们差分一下就很明显的发现我们可以在没次枚举到当前情况的时候和上一次的情况联系起来。

如果当前差分结果和上次(不知道是哪次)出现的结果一致,那么,就可以更新答案了。

不懂的话可以自己做一个关于样例的前缀和差分数组,一看就出来了。

那么当前情况拿什么来保存。数字太大了我们可以hash一下。(或者map

这就有了我82分代码:

//#include<bits/stdc++.h>
#include<iomanip>
#include<utility>
#include<cctype>
#include<vector>
#include<deque>
#include<map>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#define INF 2147483646
#define mod 100007
using namespace std;
inline long long read()
{
long long x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(long long x)
{
x<?x=-x,putchar('-'):;
long long num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const long long MAXN=;
long long a[MAXN];
long long lin[MAXN],nex[MAXN],ver[MAXN],e[MAXN],len=;
long long n,k,ans=;
long long b[MAXN][];
void hash(long long h,long long x,long long y)
{
ver[++len]=h;
nex[len]=lin[x];
lin[x]=len;
e[len]=y;
}
long long find(long long xx)
{
long long x=xx%mod;
if(lin[x]==)return -;
for(long long i=lin[x];i;i=nex[i])if(e[i]==xx)return ver[i];
return -;
}
void transform(long long h,long long x)
{
long long u=,minn=MAXN;
for(long long u=;u<=k;u++)
{
if(x&)b[h][u]=b[h-][u]+;
else b[h][u]=b[h-][u];
minn=min(b[h][u],minn);
x=x>>;
}
for(long long i=;i<=k;i++)
b[h][i]-=minn;
long long cnt=;
for(long long i=,t=;i<=k;i++,t=t*)cnt+=b[h][i]*t;
long long w=find(cnt);
if(w==-)hash(h,cnt%mod,cnt);
else ans=max(ans,h-w);
//cout<<cnt<<endl;
return; }
int main()
{
//freopen("1.in","r",stdin);
n=read();k=read();
hash(,,);
for(long long i=;i<=n;i++)
{
a[i]=read();
transform(i,a[i]);
}
put(ans);
return ;
}

很棒的代码对不对,但是只有82分,经过2h的打表发现就算采用挂链法也会导致明明不同的元素访问到。如 0(12)1这三个二进制数=25 而5(0)5=25;不一样但是hash后值是一样的。

则么办hash之后更加精细的判断即可。这样复杂度会很高。

//#include<bits/stdc++.h>
#include<iomanip>
#include<utility>
#include<cctype>
#include<vector>
#include<deque>
#include<map>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
inline long long read()
{
long long x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(long long x)
{
x<?x=-x,putchar('-'):;
long long num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const long long MAXN=;
const long long INF=;
const long long mod=;
vector<long long>q[MAXN];
long long a[MAXN];
long long lin[MAXN],nex[MAXN],ver[MAXN],e[MAXN],len=;
long long n,k,ans=;
long long b[MAXN][];
void hash(long long h,long long x,long long y,long long c[])
{
ver[++len]=h;
nex[len]=lin[x];
lin[x]=len;
e[len]=y;
for(long long i=;i<=k;++i)q[len].push_back(c[i]);
}
long long find(long long xx,long long c[])
{
long long x=xx%mod;
if(lin[x]==)return -;
for(long long i=lin[x];i;i=nex[i])
{
if(e[i]==xx)
{
bool flag=;
for(long long j=;j<=k;++j)if(c[j]!=q[i][j-]){flag=;break;}
if(flag==)return ver[i];
}
}
return -;
}
void transform(long long h,long long x)
{
long long u=,minn=INF;
for(long long u=;u<=k;++u)
{
if(x&)b[h][u]=b[h-][u]+;
else b[h][u]=b[h-][u];
minn=min(b[h][u],minn);
x=x>>;
}
for(long long i=;i<=k;++i)b[h][i]-=minn;
long long cnt=;
for(long long i=,t=;i<=k;++i,t=t<<)cnt+=b[h][i]*t;
long long w=find(cnt,b[h]);
if(w==-)hash(h,cnt%mod,cnt,b[h]);
else ans=max(ans,h-w);
return; }
int main()
{
//freopen("1.in","r",stdin);
n=read();k=read();
hash(,,,b[]);
for(long long i=;i<=n;++i)
{
a[i]=read();
transform(i,a[i]);
}
put(ans);
return ;
}

这个代码绝对很完美了,但是洛谷是可以通过的但是呢,oj上会超时的。

那么这个hash过于复杂,则么办呢。

我们可以使用map。映射直接映射到map上就会省很多的操作。

//#include<bits/stdc++.h>
#include<iomanip>
#include<utility>
#include<cctype>
#include<vector>
#include<deque>
#include<map>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#define INF 2147483646
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(int x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int MAXN=;
int n,k,ans=;
int a[MAXN];
struct wy//闻道玉门犹被遮应将性命逐轻车
{
int h;
int c[];
}t[MAXN];
map<string,int>f;
string w;
int main()
{
//freopen("1.in","r",stdin);
n=read();k=read();
for(int i=;i<=n;i++)
{
w="";int flag=;
a[i]=read();int minn=INF;
t[i].h=i;int x=a[i];
for(int j=;j<=k;j++)
{
t[i].c[j]+=t[i-].c[j];
if(x&)t[i].c[j]+=;
x=x>>;minn=min(minn,t[i].c[j]);
}
for(int j=;j<=k;j++){t[i].c[j]-=minn,w+=t[i].c[j]+'';if(t[i].c[j]!=)flag=;}
int sum=f[w];
if(flag==)ans=max(ans,i);
if(sum)ans=max(ans,i-sum);
else f[w]=i;
}
put(ans);
return ;
}

这下复杂度再次大大下降。然后oj也是可以顺利通过的,但是这里学长有一个使用了结构替友元函数的。

学习更多的东西,所以这里有一个友元函数的代码:

//#include<bits/stdc++.h>
#include<iomanip>
#include<utility>
#include<cctype>
#include<vector>
#include<deque>
#include<map>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#define INF 2147483646
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(int x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int MAXN=;
int n,k,ans=;
int a[MAXN];
struct wy//闻道玉门犹被遮应将性命逐轻车
{
int h;
int c[];
friend bool operator <(wy x,wy y)
{
for(int i=;i<=k;i++)
if(x.c[i]!=y.c[i])return x.c[i]<y.c[i];
return ;
} }t[MAXN];
map<wy,int>f;
int main()
{
//freopen("1.in","r",stdin);
n=read();k=read();
f[t[]]=;
for(int i=;i<=n;i++)
{
a[i]=read();int minn=INF;
t[i].h=i;int x=a[i];
for(int j=;j<=k;j++)
{
t[i].c[j]+=t[i-].c[j];
if(x&)t[i].c[j]+=;
x=x>>;minn=min(minn,t[i].c[j]);
}
for(int j=;j<=k;j++)t[i].c[j]-=minn;
int sum=f[t[i]];
if(sum)ans=max(ans,i+-sum);
else f[t[i]]=i+;
}
put(ans);
return ;
}

这个代码和刚刚的差不多,但是是对结构体的升华操作要学!

提交到poj上 第一个AC代码 TLE 第二个 TLE 第三个 TLE 全部都阵亡了,不服,教学长的代码,然后A了。这wy大佬是真的强啊。

经过调查不管如何进行读入、输出、卡常的优化都是TLE,然后感觉是map第一个关键字的原因的问题,因为学长的是小hash和map的结合体。

所以这道题可以考虑抄一波学长的代码他的第一个关键字是long long 类型的,所以应该要快(我也真找不出什么原因了。

而且他的hash是好hash 比我的第一个hash好上不少。完全不带重复的所以这个代码也应该是可以优化我的代码的。

所以他的代码经过我的翻译如下:

//#include<bits/stdc++.h>
#include<iomanip>
#include<utility>
#include<cctype>
#include<vector>
#include<deque>
#include<map>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#define ll long long
#define INF 2147483646
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(int x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int MAXN=,maxn=;
int n,k,ans=;
int a[MAXN][];
map<ll,int>f;
int find(int x)
{
ll sum=;
for(int i=;i<=k;i++)sum=sum*maxn+1ll*a[x][i];
if(f[sum]==&&sum!=)f[sum]=x;
return f[sum];
}
int main()
{
freopen("1.in","r",stdin);
n=read();k=read();
for(int i=;i<=n;++i)
{
int t=,x=read();
int minn=INF;
for(int j=;j<=k;j++)
{
a[i][j]=x&;a[i][j]+=a[i-][j];
x=x>>;
minn=min(minn,a[i][j]);
}
for(int j=;j<=k;j++)a[i][j]-=minn;
ans=max(ans,i-find(i));
}
put(ans);
return ;
}

很妙的hash和map的嵌套~!

刚好的map接数字较大的集合,这样就实现了快速查找。

不用map可就接不上了。总结:所以必须要用map来进行精度较高但是比结构图或者string快的东西。累了一天,了。

明天加油!ヾ(◍°∇°◍)ノ゙

hash_map的更多相关文章

  1. map,hash_map, hash_table, 红黑树 的原理和使用

    在刷算法题的时候总是碰到好多题,号称可以用hash table来解题.然后就蒙圈了. 1.首先,map和hash_map的区别和使用: (1)map底层用红黑树实现,hash_map底层用hash_t ...

  2. hash_map的简洁实现

    hash_map的简洁实现   hash_map是经常被使用的一种数据结构,而其实现方式也是多种多样.如果要求我们使用尽可能简单的方式实现hash_map,具体该如何做呢? 我们知道hash_map最 ...

  3. map vs hash_map

    1. map, multimap, set, multiset g++ 中 map, multimap, set, multiset 由红黑树实现 map: bits/stl_map.h multim ...

  4. Map和hash_map

    map和hash_map 今天在写拼流的程序时碰到一个问题,要根据流的四元组的结构信息映射到该流的数据.也就是我在网络数据包拼接的过程中,要根据包的地址和端口信息,对应到其对应的一个流的数据上去,把端 ...

  5. map,hash_map和unordered_map 实现比较

    map介绍 Map是STL[1]的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处 ...

  6. hash_map map

    什么时候需要用hash_map,什么时候需要用map? 总体来说,hash_map 查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别;而map的查找速度是log(n)级别.并不一定 ...

  7. hash_map vs unordered_map vs map vs unordered_set

    hash_map vs unordered_map 这两个的内部结构都是采用哈希表来实现.unordered_map在C++11的时候被引入标准库了,而hash_map没有,所以建议还是使用unord ...

  8. 学习hash_map从而了解如何写stl里面的hash函数和equal或者compare函数

    ---恢复内容开始--- 看到同事用unordered_map了所以找个帖子学习学习 http://blog.sina.com.cn/s/blog_4c98b9600100audq.html (一)为 ...

  9. STL中map与hash_map的比较

    1. map : C++的STL中map是使用树来做查找算法; 时间复杂度:O(log2N) 2. hash_map : 使用hash表来排列配对,hash表是使用关键字来计算表位置; 时间复杂度:O ...

  10. STL之hash_set和hash_map

    Contents 1 hash_set和hash_map的创建与遍历 2 hash_set和hash_map的查找 3 建议 一句话hash_set和hash_map:它们皆由Hashtable(St ...

随机推荐

  1. JAVA 线程池架构浅析

    经历了Java内存模型.JUC基础之AQS.CAS.Lock.并发工具类.并发容器.阻塞队列.atomic类后,我们开始JUC的最后一部分:线程池.在这个部分你将了解到下面几个部分: 线程池的基础架构 ...

  2. linux每日命令(7):rmdir命令

    rmdir是常用的命令,该命令的功能是删除空目录,一个目录被删除之前必须是空的.(注意,rm - r dir命令可代替rmdir,但是有很大危险性.)删除某目录时也必须具有对父目录的写权限. 一.命令 ...

  3. 【Linux高级驱动】触摸屏工作原理与工作流程

    触摸屏工作原理 触摸屏工作流程 @成鹏致远 (blogs:http://lcw.cnblogs.com) (email:wwwlllll@126.com) ) From WizNote

  4. 使用tar解压文件提示gzip: stdin: not in gzip format错误

    使用tar解压文件提示gzip: stdin: not in gzip format错误 1. 问题描述 使用docker save xxxx > xxx.tar导出镜像,由于文件太大,需要sp ...

  5. PentesterLab渗透演练平台

    转载自: https://www.blackh4t.org/archives/1143.html http://www.91ri.org/5958.html     1.  什么是WebApp Pen ...

  6. 【LINUX】——如何配置宿主机和虚拟机IP在同一网段

    宿主机:win7  10.8.2.50 255.255.255.0 虚拟机:redhat 如果使用 NAT 的网络连接方式,虚拟机的 IP 会被分配为 192.168.*.* 网段,从虚拟机 ping ...

  7. hdoj:2024

    #include <iostream> #include <string> #include <vector> using namespace std; int m ...

  8. c++ clr编译dll在c#调用时出现“试图加载不正确的格式”“找不到dll”错误的解决

    用depends发现缺了一堆API-MS-WIN什么的dll,网上查找是因为少了VC++2010,VC++2015等一系列,装好后仍然不行,原来这种错误并不是该原因导致的,也并不缺少那些dll(dep ...

  9. (笔记)Linux内核学习(一)之内核介绍

    内核与操作系统: 内核是操作系统的核心部分,包含了系统运行的核心过程,决定系统的性能,操作系统启动内核被装入到RAM中: 操作系统与底层硬件设备交互和为运行应用程序提供执行环境. Linux内核与微内 ...

  10. 8 -- 深入使用Spring -- 5... Spring 3.1 新增的缓存机制

    8.5 Spring 3.1 新增的缓存机制 Spring 3.1 新增了一种全新的缓存机制,这种缓存机制与Spring容器无缝地整合在一起,可以对容器中的任意Bean或Bean的方法增加缓存.Spr ...