D
模拟 stl
每次朝一个方向移动x步,有n个障碍,如果有障碍,会被长挡住。问执行m个指令后的结束位置?
有障碍会被挡住,需要我们快速查找移动路径上是否有障碍,这可以set二分,找到大于当前位置的最小障碍/小于当前位置的最大障碍,然后看障碍位置是否在本次移动的路径上。
这样需要对每一行维护所有障碍的y坐标,每一列维护所有障碍的x坐标。考虑到坐标范围很大,行列号也需要离散化,故考虑map<int,set<int>>
E
可持久化思想
一个初始为空的序列,可以插入,删除一个元素,保存,读取副本。
副本肯定不能真的把当前序列存下来,这样就O(q2)O(q^2)O(q2)了。
注意到如果只插入就是一条链,如果删除几个,然后再插入,相当于回退到链上某一点,然后从这一点产生一个新的分支,这样的结构实际上是一棵树。维护这样一颗树即可
如果插入,就再当前节点下面插入一个叶子,如果删除,就回到父亲,如果保存副本,就记录第i个副本,对应的是当前节点编号,读取第i个副本,就是修改当前节点编号为,第i个副本保存的节点。
这样的好处是,我们每次读取副本,实际上是转移到一条链的结尾,而前面的元素,可以多个链共享。
这其实是类似可持久化线段树的思想,可持久化线段树也是多个副本共享无修改的部分,每个副本只在自己修改的部分增加一些节点。
void solve(void){
int q;
cin>>q;
int cur=-1;
vi fa(q+1);
vi node;
map<int,int>mp;
rep(i,1,q){
string op;
cin>>op;
int x;
if(op=="ADD"){
cin>>x;
node.push_back(x);
fa[node.size()-1]=cur;
cur=node.size()-1;
}
else if(op=="DELETE"){
if(cur!=-1)cur=fa[cur];
}
else if(op=="SAVE"){
cin>>x;
mp[x]=cur;
}
else{
cin>>x;
if(!mp.count(x))mp[x]=-1;
cur=mp[x];
}
if(cur==-1)cout<<-1<<' ';
else cout<<node[cur]<<' ';
}
}
F
区间DP
数轴上,n个位置有墙,n个位置有锤子,一个锤子可以打破对应一个墙。问从原点到达x的最短用时?
在一个数轴上,或在一个序列上的问题,可以考虑区间dp。
dp(i,j)dp(i,j)dp(i,j)表示使得[i,j][i,j][i,j]内全部可达的最小代价。这样最后答案就是所有包含终点的区间的dp值最小值。
然后只有这些信息,无法转移,因为不知道当前在哪,状态里必须保存一下位置,但是选择区间内哪个位置呢?考虑到使得一个区间全部可达,最优走法最后肯定停在区间端点。所以状态记录最后停在左端点还是右端点。这样转移就是从一个区间端点,移动到新的区间的端点。
转移时,区间往外拓展一个位置,当新位置没有墙,或者油枪,但是墙对应地锤子在当前区间内部,则可以转移。
墙的坐标可能是负数,且范围很大,考虑离散化。由于计算距离要用原始数据,考虑使用排序二分的方式离散化,可以保存原始数据。
int f[3030][3020][2];
void solve(void){
int n,x;
cin>>n>>x;
memset(f,0x3f,sizeof f);
vi a(n+1),b(n+1),all;
all.push_back(-1e18);
rep(i,1,n){
cin>>a[i];
all.push_back(a[i]);
}
rep(i,1,n){
cin>>b[i];
all.push_back(b[i]);
}
int s=0;
all.push_back(s);
all.push_back(x);
sort(all.begin(),all.end());
all.erase(unique(all.begin(),all.end()),all.end());
int m=all.size()-1;
vi id(m*2+10);
rep(i,1,n){
a[i]=lower_bound(all.begin(),all.end(),a[i])-all.begin();
b[i]=lower_bound(all.begin(),all.end(),b[i])-all.begin();
id[a[i]]=i;
}
s=lower_bound(all.begin(),all.end(),s)-all.begin();
x=lower_bound(all.begin(),all.end(),x)-all.begin();
f[s][s][0]=f[s][s][1]=0;
rep(i,1,m-1){
rep(l,1,m){
int r=l+i-1;
if(l>1){
int p=id[l-1];
if(!p||b[p]>=l&&b[p]<=r){
f[l-1][r][0]=min(f[l][r][0]+all[l]-all[l-1],f[l-1][r][0]);
f[l-1][r][0]=min(f[l][r][1]+all[r]-all[l-1],f[l-1][r][0]);
}
}
if(r<m){
int p=id[r+1];
if(!p||b[p]>=l&&b[p]<=r){
f[l][r+1][1]=min(f[l][r][1]+all[r+1]-all[r],f[l][r+1][1]);
f[l][r+1][1]=min(f[l][r][0]+all[r+1]-all[l],f[l][r+1][1]);
}
}
}
}
int ans=1e18;
rep(i,1,x){
rep(j,x,m){
ans=min({ans,f[i][j][0],f[i][j][1]});
}
}
if(ans<1e18)cout<<ans;
else cout<<-1;
}