离散优化
应用
解决有已知的固定输入(即没有在线插入,离线插入所有数据,计算过程不产生数据),且范围较大的情况,通常需要的是排名而不是具体数值,因此直接按大小重新赋予每个数一个新值。
模板
for(int i=1;i<=n;++i){ scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1);//排序 int tot=unique(b+1,b+n+1)-b-1;//去重 for(int i=1;i<=n;++i)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;//重定义位置
哈希优化
应用
解决有查询且范围比较大的情况,会需要具体数值。
当解决动态在线问题时(可能是计算过程中产生数据)需要建立可插入的hash链表,离线问题可以直接用数组存储,从头到尾扫一遍判重。
本质就是将特别大的数通过h(k)的方式映射为新的小数值,并存储到链表或数组中。(若是链表则范围是链头数组大小的范围;若是数组则范围是int(long long)的范围。)
不是普通数的也可以进行hash优化,例如数字字符串就可以用hash优化存储。(尤其是字符串中数字特别大的情况,ASCII码存不下)
~~在动态查询上因为码量而完胜平衡树~~
P2852
通过p和P解决问题。
定义p=1000017(10007也行,就是1e6不需要进位,更直观),P=1000000007,取了两个质数防止冲突。
因此将问题转化为:将字符串以p进制数的方式存储为小于P的数。(这个过程就是hash的核心:将特别大的数通过h(k)的方式映射为新的小数值,并存储。)
注意存储的是p进制数在10进制表示下 mod P的结果。
判断是排个序就行。
然后二分,没了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int N=3e4+10,p=10007,P=1000000007;
int a[N],b[N],c[N],d[N];
int n,k;
void init(){
b[0]=0;
c[0]=1;
for(int i=1;i<=n;++i){
b[i]=(b[i-1]*p+a[i])%P;
}
for(int i=1;i<=n;++i){
c[i]=(c[i-1]*p)%P;
}
return;
}
bool cmp(int a,int b){
return a<b;//:)
}
bool check(int x){
memset(d,0,sizeof d);
int top=0;
for(int i=x;i<=n;++i){
d[++top]=(b[i]-b[i-x]*c[x])%P;
if(d[top]<0)d[top]+=P;
}
sort(d+1,d+top+1,cmp);
int cnt=0,ans=0;
for(int i=1;i<=top;++i){
cnt++;
if(d[i+1]!=d[i] || i==top){
ans=max(ans,cnt);
cnt=0;
}
}
return ans>=k;
}
int solve(){
int l=0,r=n+1,ans=0;
while(l<r){
int mid=l+r>>1;
if(check(mid)){
ans=max(ans,mid);
l=mid+1;
}else r=mid;
}
return ans;
}
signed main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
init();
int ans=solve();
printf("%lld",ans);
return 0;
}
康托展开
本质也是一种hash优化,这次的映射函数为cantor(k)。对于一个全排列,其康托展开为 $$ cantor(k)=\sum_{k=n}^1 f(a[k])* (k-1)!$$ f(a[k])指比a[k]小的,未出现的数的个数。 例如34512值为61,所以是第62个。
P1379
因为总共的状态一共9!=362880种,且后进队的重复状态一定没有之前的界更优,所以直接剪枝bfs即可,用cantor(k)判断是否入队。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define int long long
using namespace std;
const int N=4e5+10;
int tmp[3][3],d[3][3]={1,2,3,8,0,4,7,6,5};
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int vis[9],vist[N];
int l[10];
int n,goal;
struct node{
int v;
int step;
int d[3][3];
node(int vv,int s,int dd[3][3]){
v=vv;
step=s;
for(int i=0;i<3;++i){
for(int j=0;j<3;++j)d[i][j]=dd[i][j];
}
}
};
int cantor(int d[3][3]){//3x3->cantor
memset(vis,0,sizeof vis);
int ans=0;
for(int i=0;i<3;++i){
for(int j=0;j<3;++j){
int cnt=0;
for(int k=0;k<d[i][j];++k)if(vis[k])cnt++;
ans+=(d[i][j]-cnt)*l[9-i*3-j-1];
vis[d[i][j]]=1;
}
}
return ans;
}
void get1(int n){//int->3x3
for(int i=2;i>=0;--i){
for(int j=2;j>=0;--j){
d[i][j]=n%10;
n/=10;
}
}
return;
}
void init(){
l[0]=l[1]=1;
for(int i=2;i<=9;++i)l[i]=l[i-1]*i;
goal=cantor(d);
return;
}
int bfs(){
memset(vist,0,sizeof vist);
queue<node> q;
get1(n);
while(!q.empty())q.pop();
int a=cantor(d);
q.push(node(a,0,d));
vist[a]=1;
while(!q.empty()){
memcpy(d,q.front().d,sizeof q.front().d);
int step=q.front().step;
int a=cantor(d);
if(a==goal)return step;
int x=0,y=0;
for(int i=0;i<3;++i){
for(int j=0;j<3;++j){
if(d[i][j]==0){
x=i,y=j;
break;
}
}
}
q.pop();
for(int i=0;i<4;++i){
int xx=dx[i]+x,yy=y+dy[i];
if(xx>=0 && xx<3 && yy>=0 && yy<3){
memcpy(tmp,d,sizeof d);
swap(tmp[x][y],tmp[xx][yy]);
int a=cantor(tmp);
if(a==goal)return step+1;
if(!vist[a]){
vist[a]=1;
q.push(node(a,step+1,tmp));
}
}
}
}
return -1;
}
signed main(){
init();
if(goal!=46685)goal=46685;
scanf("%lld",&n);
printf("%lld",bfs());
return 0;
}