bzoj3126 [Usaco2013 Open]Photo
省选前最后一天填坑......
其实这个题早就想过,一直懒得写。我发现我写单调队列一次一个样,真是郁闷。
简述题意就是给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点。
我们考虑用f[i]表示前i个位置能放多少个,每一步决策有两个限制:1.每个区间里要有一个点,这样我们可以确定决策区间左边界;2.每个区间里只能有一个点,这样我们可以确定右边界。如果有地方左边界大于右边界了就是一定不能放点了。
单调队列的话,首先让[ L[I],R[I] ]区间的f值入队,然后把队首不在这个决策区间里的出队,然后转移f[q[head]]就行了。
总结起来,单队大概有两种,一种是先转移再入队,另一种是先入队再转移。前一种大多是针对斜率优化的,因为每个点对应一个斜率来T掉队里点的,也就是说每枚举到一个i就要更新一下单队,而每得到一个新的f值下一步立即就可以参与转移;而后一种大多是针对决策单调性的,因为每个点对应一个决策区间,这个区间是不断移动的,所以每得到一个f值是不能立即参与下一步转移的,而是要通过决策区间的移动来确定单队。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 220000 7 #define inf 2147483647 8 using namespace std; 9 10 struct seg 11 { 12 int x,y; 13 }a[maxn]; 14 int l[maxn],r[maxn],q[maxn*2],f[maxn]; 15 int n,m; 16 17 bool cmp(seg a,seg b) 18 { 19 return a.y<b.y; 20 } 21 22 int main() 23 { 24 //freopen("photo.in","r",stdin); 25 scanf("%d%d",&n,&m); 26 for (int i=1;i<=m;i++) 27 scanf("%d%d",&a[i].x,&a[i].y); 28 sort(a+1,a+m+1,cmp); 29 int mx=0; 30 for (int i=1,j=1;i<=n;i++) 31 { 32 while (a[j].y<i&&j<=m) mx=max(mx,a[j].x),j++; 33 l[i]=mx; 34 } 35 l[n+1]=1; 36 int mi=n; 37 for (int i=n,j=m;i;i--) 38 { 39 while (a[j].y>=i&&j) mi=min(mi,a[j].x-1),j--; 40 r[i]=mi; 41 } 42 r[n+1]=n; 43 //for (int i=1;i<=n;i++) cout<<l[i]<<' '<<r[i]<<endl; 44 int head=1,tail=0; 45 for (int i=1,j=0;i<=n+1;i++) 46 { 47 for (;j<=r[i]&&j<i;j++) 48 { 49 if (f[j]==-1) continue; 50 while (head<=tail&&f[j]>f[q[tail]]) tail--; 51 q[++tail]=j; 52 } 53 while (head<=tail&&q[head]<l[i]) head++; 54 if (head<=tail) f[i]=f[q[head]]+(i!=n+1); else f[i]=-1; 55 } 56 printf("%d\n",f[n+1]); 57 return 0; 58 }
AC without art, no better than WA !