【学习笔记】NOI 模拟赛 t3 point

点这里看题目

考场上也没想到这道 t 3 t3 t3竟是整场比赛最可做的一道题。但是拿了全场最高的部分分,这是好的。

唉,学了插头 d p dp dp但是却无法将其运用,我真傻,真的。

部分分已经很好的提示了这道题的正解是状压。先考虑 d > 8 d>8 d>8,也就是我考场上没想出来的部分。

不妨将原问题做细微的变动,令 d ← 2 d d\gets 2d d2d,那么 x x x可以移动到 x x x x + 2 d x+2d x+2d两个位置,这样做的好处在于所有点都是朝一个方向运动。

以往我们对坐标的认识都是很大而不好处理的,但是这道题非常特殊,注意到 x i ≤ 150 x_i\le 150 xi150,因此可以直接看成是大小 ≤ 150 \le 150 150的棋盘,特别的,我们将 x , x + d , x + 2 d , . . . x,x+d,x+2d,... x,x+d,x+2d,...排成一个横排,这样得到了一个宽 ≤ ⌊ x i d ⌋ \le \lfloor\frac{x_i}{d}\rfloor dxi的矩阵。

注意到 ⌊ x i d ⌋ ≤ 8 \lfloor\frac{x_i}{d}\rfloor\le 8 dxi8,这提示我们按行进行状压。但是同一行的坐标在数轴上不是连续的,注意到我们在插头 d p dp dp中处理的路径也不是连续的,但是我们可以将它拼接起来,那么类似的,我们也可以看成是数轴上有若干个端点朝左右方向扩展,新建一个端点的代价是 a a a,扩展一个单位的代价是 b b b。甚至这道题目的情况也远没有插头 D P DP DP复杂,只是这个想法比较隐晦而已。

观察一下这个表格,同一列在坐标轴上的位置是固定的,因此可以只用记录两个端点是否被线段覆盖。一个棋子也只能移动到同一行相邻的位置,因此情况并不复杂。那么这部分就做完了,复杂度 O ( max ⁡ ( x i ) 2 16 ) O(\max(x_i)2^{16}) O(max(xi)216)

最后再来补充一下 d ≤ 8 d\le 8 d8的部分。不得不说这个部分分设置得非常好。考场上想了一个巨麻烦的方法,但是其实没有必要,就直接记录后 d d d个位置那些点有棋子,以及前一个位置有没有被覆盖即可,这其实就是我考场的做法,但是考场上好像写复杂了。复杂度同样是 O ( max ⁡ ( x i ) 2 16 ) O(\max(x_i)2^{16}) O(max(xi)216)

代码比想象中的要长一点。注意数组不要开小了。

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define db double #define inf 0x3f3f3f3f using namespace std; int n,d,a,b,X[155],now[2][1<<18],nxt[2][1<<18],vispoints[305]; int h,w,now2[1<<9],nxt2[1<<9],points[305][20],res=0x3f3f3f3f; void chmin(int &x,int y){x=min(x,y);} signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>d>>a>>b,d<<=1; for(int i=0;i<n;i++)cin>>X[i]; sort(X,X+n); n=unique(X,X+n)-X;for(int i=n-1;i>=0;i--)X[i]-=X[0]; if(d<=18){ memset(now,0x3f,sizeof now); now[0][0]=0; for(int i=0;i<n;i++)vispoints[X[i]]=1; for(int i=0;i<=X[n-1]+d;i++){ memset(nxt,0x3f,sizeof nxt); if(!vispoints[i]){ for(int j=0;j<1<<d;j++){ if(j&1){ chmin(nxt[1][j>>1],now[0][j]+a); chmin(nxt[1][j>>1],now[1][j]+min(a,b)); } else{ chmin(nxt[1][j>>1],now[0][j]+a); chmin(nxt[1][j>>1],now[1][j]+min(a,b)); chmin(nxt[0][j>>1],now[0][j]); chmin(nxt[0][j>>1],now[1][j]); } } } else{ for(int j=0;j<1<<d;j++){ if(j&1){ chmin(nxt[1][j>>1],now[0][j]+a); chmin(nxt[1][j>>1],now[1][j]+min(a,b)); } else{ chmin(nxt[1][j>>1],now[0][j]+a); chmin(nxt[1][j>>1],now[1][j]+min(a,b)); chmin(nxt[0][(j>>1)^(1<<d-1)],now[0][j]); chmin(nxt[0][(j>>1)^(1<<d-1)],now[1][j]); } } } memcpy(now,nxt,sizeof nxt); } cout<<now[0][0]; } else{ h=d,w=X[n-1]/d+2; assert(2*w<=18); for(int i=0;i<n;i++){ points[X[i]%d][X[i]/d]=1; assert(X[i]/d<w-1); assert(X[i]%d<h); } for(int s=0;s<1<<w;s++){ int ok=1; for(int i=0;i<w-1;i++){ if(points[0][i]&&!((s>>i&1)|(s>>i+1&1))){ ok=0; break; } } if(!ok)continue; int tmp=__builtin_popcount(s); memset(now2,0x3f,sizeof now2),now2[s]=tmp*a; for(int i=1;i<h;i++){ for(int j=0;j<w;j++){ memset(nxt2,0x3f,sizeof nxt2); for(int k=0;k<1<<w;k++){ if(j&&points[i][j-1]&&!(k>>j-1&1)){ if(k>>j&1){ chmin(nxt2[k],now2[k]+min(a,b)); } else{ chmin(nxt2[k^(1<<j)],now2[k]+a); } } else{ if(k>>j&1){ chmin(nxt2[k],now2[k]+min(a,b)); chmin(nxt2[k^(1<<j)],now2[k]); } else{ chmin(nxt2[k^(1<<j)],now2[k]+a); chmin(nxt2[k],now2[k]); } } } memcpy(now2,nxt2,sizeof nxt2); } } for(int i=0;i<1<<w;i++){ for(int j=0;j<w-1;j++){ if((i>>j&1)&&(s>>j+1&1)&&a>=b){ now2[i]-=a,now2[i]+=b; } } chmin(res,now2[i]); } } cout<<res; } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529957.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示