$n \leq 1e9$,$n*2$的网格里有$m_1+m_2 \leq 1e6$个障碍物,现有一坦克从$(0,1)$出发要到$(n+1,1/2)$,他每秒可以换行(纵坐标1变2或2变1)也可以发炮弹,然后会强制往前走一格(横坐标+1),一次炮弹打一个障碍,炮弹装载时间$t \leq n$,一开始炮弹没装好。问是否可以到达终点,若可以输出过程中的所有转弯和打炮操作的位置。
比较接地气的一个题。虽然没做出来但大爱。
首先可以把打炮看成“攒步数”,怎么讲呢?比如说坦克在某段时间一直往前走不换行,走到一个障碍物就不得不打他一下,那么他在撞到这障碍物前至少有$t$步空闲。类似地,两个障碍物就$2t$,$k$个就$kt$,就好比在攒步数,然后到障碍物处花掉。而换了行的话,由于不可以换行立即猛发炮弹(尽管之前攒了很多步数但刚换行最多只能打一发),因此步数将变成$min(当前步数,t)$。基于此可以进行一个dp:$f(i,j)$表示在格子$(i,j)$攒的步数,无法到达即$-1$。注意分是否是障碍物转移,因为一行有障碍物时另一行的坦克不可能直接撞上来。这样就得到了$O(n)$的解法。
$However$,这里$n$大大的!必须把复杂度与障碍物数联系起来!
其实dp的主要瓶颈在换行,如果不换行完全可以枚举每个障碍物,到那里$f(i,j)-=t$即可。那换行咋整啊?
yy可得:存在一最优策略使得换行操作一定在某个障碍物后进行。如下图所示,黑色块是某个转弯前的最后一个块,绿色的线路和红色线路攒的步数是一样的,但由于换行之后dp数组会对$t$取$min$,因此不如红色线路。
是一个贪心策略。这种dp+贪心是比较新颖的套路。
转移时注意,因为我表示的状态是“每个障碍物的下一列”,如果下一列有障碍物要提前打掉。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 //#include<map> 6 #include<math.h> 7 //#include<time.h> 8 //#include<complex> 9 #include<algorithm> 10 using namespace std; 11 12 int n,m1,m2,t; 13 #define maxn 2000011 14 bool turn[2][maxn]; int list[2][maxn],f[2][maxn]; 15 struct BO{int pos,type;}bo[maxn],ans[maxn]; int lb=0; 16 int lans,tt[maxn],ltt; 17 bool cmp(const BO a,const BO b) {return a.pos<b.pos;} 18 19 int main() 20 { 21 scanf("%d%d%d%d",&n,&m1,&m2,&t); 22 for (int i=1;i<=m1;i++) scanf("%d",&list[0][i]); 23 for (int i=1;i<=m2;i++) scanf("%d",&list[1][i]); 24 for (int i=1,j=1;;) 25 { 26 if (i>m1) {for (;j<=m2;j++) bo[++lb]=(BO){list[1][j],1}; break;} 27 if (j>m2) {for (;i<=m1;i++) bo[++lb]=(BO){list[0][i],0}; break;} 28 if (list[0][i]<list[1][j]) bo[++lb]=(BO){list[0][i++],0}; 29 else if (list[0][i]>list[1][j]) bo[++lb]=(BO){list[1][j++],1}; 30 else bo[++lb]=(BO){list[0][i++],2},j++; 31 } 32 33 f[0][0]=f[1][0]=0; bo[0].pos=-1; bo[lb+1].pos=n+1; bo[lb+1].type=3; 34 for (int i=1;i<=lb;i++) 35 { 36 if (bo[i-1].pos!=bo[i].pos-1) 37 { 38 f[0][i]=f[0][i-1]==-1?-1:f[0][i-1]+(bo[i].pos-bo[i-1].pos)-2; 39 f[1][i]=f[1][i-1]==-1?-1:f[1][i-1]+(bo[i].pos-bo[i-1].pos)-2; 40 if (bo[i].type==0 || bo[i].type==2) f[0][i]=max(-1,f[0][i]-t); 41 if (bo[i].type==1 || bo[i].type==2) f[1][i]=max(-1,f[1][i]-t); 42 f[0][i]=f[0][i]==-1?-1:f[0][i]+1; 43 f[1][i]=f[1][i]==-1?-1:f[1][i]+1; 44 } 45 else f[0][i]=f[0][i-1],f[1][i]=f[1][i-1]; 46 if (bo[i+1].pos==bo[i].pos+1) 47 { 48 if (bo[i+1].type==0 || bo[i+1].type==2) f[0][i]=max(-1,f[0][i]-t); 49 if (bo[i+1].type==1 || bo[i+1].type==2) f[1][i]=max(-1,f[1][i]-t); 50 } 51 f[0][i]=f[0][i]==-1?-1:f[0][i]+1; 52 f[1][i]=f[1][i]==-1?-1:f[1][i]+1; 53 if (bo[i+1].pos!=bo[i].pos+1 || bo[i+1].type==1 || bo[i+1].type==3) 54 {if (f[0][i]<min(t,f[1][i])) turn[0][i]=1,f[0][i]=min(t,f[1][i]);} 55 if (bo[i+1].pos!=bo[i].pos+1 || bo[i+1].type==0 || bo[i+1].type==3) 56 {if (f[1][i]<min(t,f[0][i])) turn[1][i]=1,f[1][i]=min(t,f[0][i]);} 57 } 58 if (f[0][lb]==-1 && f[1][lb]==-1) {puts("No"); return 0;} 59 puts("Yes"); 60 int now=0; if (f[0][lb]==-1) now=1; lans=ltt=0; int cnt=0; 61 for (int i=lb;i;i--) 62 { 63 if (turn[now][i]) 64 { 65 tt[++ltt]=bo[i].pos+1; 66 for (int j=bo[i].pos+1+t-f[now][i];cnt;j+=t,cnt--) ans[++lans]=(BO){j,now}; 67 now^=1; if (bo[i+1].pos==bo[i].pos+1 && (bo[i+1].type==now || bo[i+1].type==2)) cnt++; 68 } 69 if (bo[i].type==now || bo[i].type==2) cnt++; 70 } 71 if (now==1) tt[++ltt]=0; 72 for (int j=t;cnt;j+=t,cnt--) ans[++lans]=(BO){j,now}; 73 printf("%d\n",ltt); 74 for (int i=ltt;i;i--) printf("%d ",tt[i]); puts(""); 75 sort(ans+1,ans+1+lans,cmp); 76 printf("%d\n",lans); 77 for (int i=1;i<=lans;i++) printf("%d %d\n",ans[i].pos,ans[i].type+1); 78 return 0; 79 }