标签:
大sz最近在玩一个由星球大战改编的游戏。话说绝地武士当前共控制了N个星球。但是,西斯正在暗处悄悄地准备他们的复仇计划。绝地评议会也感觉到了这件事。于是,准备加派绝地武士到各星球防止西斯的突袭。一个星球受到攻击以后,会尽快通知到总基地。需要的时间越长的星球就需要越多绝地武士来防御。为了合理分配有限的武士,大sz需要你帮他求出每个星球各需要多少时间能够通知到总基地。由于某种原因,N个星球排成一条直线,编号1至N。其中总基地建在1号星球上。每个星球虽然都是绝地武士控制的,但是上面居住的生物不一定相同,并且科技水平也不一样。第i个星球能收到并分析波长在[xi, yi]之间的信号,并且也能够发出在这个区间的信号,但是不能发出其他任何波长的信号。由于技术原因,每个星球只能发信号到比自己编号小的距离不超过L的星球。特别地,强大的总基地可以接收任何波长的信号。每个星球处理接收到的数据需要1个单位时间,传输时间可以忽略不计。
第一行两个正整数N、L。接下来N-1行,总共第i行包含了三个正整数xi、yi、li,其中li表示第i个星球距离1号星球li,满足li严格递增。
总共N-1行,每行一个数分别表示2到N号星球至少需要多少单位时间,总基地能够处理好数据,如果无法传到总基地则输出-1。
线段树标记永久化+单调队列+DP
首先一眼DP: $dp[i]=min(dp[j])+1$ 其中 $\left | l[i]-l[j] \right |<L,\left [ x[i],y[i] \right ]\bigcap \left [ x[j],y[j] \right ]\neq \O $
那么需要优化复杂度,考虑利用数据结构(可以利用很多种,这里选用权值线段树+单调队列)
x[],y[]范围过大,单很稀疏,离散,建权值线段树,支持区间修改区间查询;维护区间中的答案,需要在区间中加入一个单调队列
发现标记不适合下传,即标记需要永久化,和之前维护直线的思想类似,这样复杂度就一样能保证在$O(nlogn)$
如果当前区间完全覆盖,则不需要下传;维护的最小值,即区间的单调队列队首,和左右区间的最小三者取最小
查询的时候查询有交集的所有区间,线段树中节点的信息只是维护了完全包含于这个区间的区间的信息,我们还需要知道和这个区间有交集的但不包含于这个区间的信息,所以往子树递归的时候把路径上的信息也一块统计就好啦。
总结:
1.标记永久化的思想更加加深
2.对于类似这样的单调的问题,同样可以用线段树去维护,据说类似问题线段树是比CDQ之类还要无敌的??
3.对于每个线段树区间套上单调队列时,容易爆,开始手写单调队列,炸编译,改成动态RE,所以可以考虑用<list>
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<list> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘)f=-1; ch=getchar();} while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();} return x*f; } #define maxn 250010 #define inf 0x7fffffff int N,L,tot,ls[maxn<<1],val[maxn<<4],dp[maxn],que[maxn],x[maxn],y[maxn],l[maxn]; struct DQueueNode{list<int>q;}Tree[maxn<<4]; int Get(int now) {return !Tree[now].q.empty()?dp[Tree[now].q.front()]:inf;} void update(int now,int l,int r) {val[now]=min(Get(now),(l==r)?inf:min(val[now<<1],val[now<<1|1]));} void Build(int now,int l,int r) { val[now]=inf; if (l==r) return; int mid=(l+r)>>1; Build(now<<1,l,mid); Build(now<<1|1,mid+1,r); } void Insert(int now,int l,int r,int L,int R,int x,int f) { if (L<=l && R>=r) { if (f) while (!Tree[now].q.empty() && Tree[now].q.front()<=x) Tree[now].q.pop_front(); else {while (!Tree[now].q.empty() && dp[Tree[now].q.back()]>=dp[x]) Tree[now].q.pop_back(); Tree[now].q.push_back(x);} update(now,l,r); return; } int mid=(l+r)>>1; if (L<=mid) Insert(now<<1,l,mid,L,R,x,f); if (R>mid) Insert(now<<1|1,mid+1,r,L,R,x,f); update(now,l,r); } int Query(int now,int l,int r,int L,int R) { if (L<=l && R>=r) return val[now]; int mid=(l+r)>>1,re=Get(now); if (L<=mid) re=min(re,Query(now<<1,l,mid,L,R)); if (R>mid) re=min(re,Query(now<<1|1,mid+1,r,L,R)); return re; } int main() { N=read(),L=read(); for (int i=1; i<=N-1; i++) ls[++tot]=x[i]=read(),ls[++tot]=y[i]=read(),l[i]=read(); sort(ls+1,ls+tot+1); int Tot=1; for (int i=2; i<=tot; i++) if (ls[i]!=ls[i-1]) ls[++Tot]=ls[i]; for (int i=1; i<=N-1; i++) x[i]=lower_bound(ls+1,ls+Tot+1,x[i])-ls,y[i]=lower_bound(ls+1,ls+Tot+1,y[i])-ls; Tot=unique(ls+1,ls+Tot+1)-ls-1; // for (int i=1; i<=N-1; i++) printf("%d %d\n",x[i],y[i]); Build(1,1,Tot); int he=-1,ta=-1; dp[0]=0; x[0]=1,y[0]=Tot; que[++ta]=0; Insert(1,1,Tot,x[0],y[0],0,0); for (int i=1; i<=N-1; i++) { int tmp; while (he<ta && l[i]-l[que[he+1]]>L) tmp=que[++he],Insert(1,1,Tot,x[tmp],y[tmp],tmp,1); dp[i]=Query(1,1,Tot,x[i],y[i])+1; if (dp[i]!=inf+1) printf("%d\n",dp[i]),que[++ta]=i,Insert(1,1,Tot,x[i],y[i],i,0); else puts("-1"); } return 0; }
【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP
标签:
原文地址:http://www.cnblogs.com/DaD3zZ-Beyonder/p/5480630.html