hdu - 4343 - Interval query - dp(贪心)
来源:互联网 发布:折八百淘宝商城女装 编辑:程序博客网 时间:2024/06/04 18:34
Description
给定N(N<=100000)个区间(左闭右开)和M(M<=100000)个询问[l, r],问所有满足[s,t)包含于于[l, r]的区间中最多能选出多少个,使得他们两两不相交。
Solution
首先是一个贪心的想法,如果存在两个区间[s1,t1)和[s2,t2),且[s1,t1)包含于[s2,t2),那么[s2,t2)是可以扔掉的。很显然地,如果[s2,t2)包含于某个解中,那么把它换成[s1,t1)肯定个数不变。
所以就可以把包含了其它区间的那些区间去掉。具体做法是天王想的,特别抽,详见代码。
其实这步做不做无所谓,因为最坏情况下不会扔掉任何一个区间。但事实证明做了这一步就可以O(N*M)暴力水过……
剩下来的区间按左端点升序排序,那么右端点一定也是升序的。
然后求方案其实还是一个贪心的想法,对于一个询问[l, r],设i=max{i|t[i]<=r},如果s[i]>=l,那么就选上i,然后设prev[i]=max{j|j<i且t[j]<=s[i]},也就是prev[i]为i之前最右边的和i不相交的区间,如果s[prev[i]]>=l,那么就选上prev[i]。就这样不停贪心地向前找,直到找到某个j=max{j|s[j]<l},那么j就不能选了。这么做的正确性也很显然,就不证了。
于是我们发现,这些线段就形成了一个树型结构,除了第一个线段,每个线段都有其前继。线段的排序是这颗有向树的拓扑排序的一种。
如果将询问排序按右端点排序后,容易用均摊线性时间找到对于询问[l, r]的终止节点i=max{i|t[i]<=r}。那么接下来就是顺着前继向前找,直到发现j=max{j|s[j]<l}。
暴力找的复杂度是O(N),有M个询问,也就是总共是O(N*M)。由于数据比较水,在第一个贪心思路把没用的区间去掉后可以750ms通过(RunID=6472102,似乎比不加这个优化然后正经做的都要快……)。
稍微思考一下。顺着前继向前找,一直找到最左边,这样找到的节点形成了一条链。很显然,链上的左端点是单调的,这给了我们二分的启示。
但是普通的二分需要存储每一条链,这在时间和空间上都是不允许的。
将ans进行二进制表示,令ans=2^k*b[k]+2^(k-1)*b[k-1]+...+2^0*b[0],b[t]=0或1, t=k,k-1,...,0,可以发现,二分的过程事实上就是根据当前的ans判断出每一个b[t], t=k,k-1,...,0是1还是0的过程。
题目大意:求区间最多有多少不相交线段。
给定N(N<=100000)个区间(左闭右开)和M(M<=100000)个询问[l, r],问所有满足[s,t)包含于于[l, r]的区间中最多能选出多少个,使得他们两两不相交。
Solution
首先是一个贪心的想法,如果存在两个区间[s1,t1)和[s2,t2),且[s1,t1)包含于[s2,t2),那么[s2,t2)是可以扔掉的。很显然地,如果[s2,t2)包含于某个解中,那么把它换成[s1,t1)肯定个数不变。
所以就可以把包含了其它区间的那些区间去掉。具体做法是天王想的,特别抽,详见代码。
其实这步做不做无所谓,因为最坏情况下不会扔掉任何一个区间。但事实证明做了这一步就可以O(N*M)暴力水过……
剩下来的区间按左端点升序排序,那么右端点一定也是升序的。
然后求方案其实还是一个贪心的想法,对于一个询问[l, r],设i=max{i|t[i]<=r},如果s[i]>=l,那么就选上i,然后设prev[i]=max{j|j<i且t[j]<=s[i]},也就是prev[i]为i之前最右边的和i不相交的区间,如果s[prev[i]]>=l,那么就选上prev[i]。就这样不停贪心地向前找,直到找到某个j=max{j|s[j]<l},那么j就不能选了。这么做的正确性也很显然,就不证了。
于是我们发现,这些线段就形成了一个树型结构,除了第一个线段,每个线段都有其前继。线段的排序是这颗有向树的拓扑排序的一种。
如果将询问排序按右端点排序后,容易用均摊线性时间找到对于询问[l, r]的终止节点i=max{i|t[i]<=r}。那么接下来就是顺着前继向前找,直到发现j=max{j|s[j]<l}。
暴力找的复杂度是O(N),有M个询问,也就是总共是O(N*M)。由于数据比较水,在第一个贪心思路把没用的区间去掉后可以750ms通过(RunID=6472102,似乎比不加这个优化然后正经做的都要快……)。
稍微思考一下。顺着前继向前找,一直找到最左边,这样找到的节点形成了一条链。很显然,链上的左端点是单调的,这给了我们二分的启示。
但是普通的二分需要存储每一条链,这在时间和空间上都是不允许的。
将ans进行二进制表示,令ans=2^k*b[k]+2^(k-1)*b[k-1]+...+2^0*b[0],b[t]=0或1, t=k,k-1,...,0,可以发现,二分的过程事实上就是根据当前的ans判断出每一个b[t], t=k,k-1,...,0是1还是0的过程。
这样就可以利用类似于最近公共祖先(LCA)的倍增法的思路,记录i的2^j祖先,也就是i节点向前找2^j是什么节点。然后就可以根据每次向前找2^j, j=k,k-1,...,0后,节点的左端点是否还满足s[j]>=l来判断ans的二进制表示中第j位是1还是0了。
#include <cstdio>#include <cstring>#include <vector>#include <algorithm>using namespace std;struct ss{ ss() {} ss(int a, int b): s(a), t(b) {} int s, t; int p, q; bool vis; bool operator<(const ss &rhs)const { return t<rhs.t; } void write() { printf("%d %d\n", s, t); }};bool cmp(const ss &u, const ss &v){ if (u.p==v.p) return u.q>v.q; return u.p<v.p;}struct sq{ int id; int s, t; bool operator<(const sq &rhs) const { return t<rhs.t; }} q[100010];const int INF = 1000000001;int N, M;ss hmr[100010], mdk[100010];int ans[100010];int prev[100010][22];int main(){ int i, j, k; while (~scanf("%d%d", &N, &M)) { for (i = 0; i < N; ++i) { scanf("%d%d", &hmr[i].s, &hmr[i].t); hmr[i].p = hmr[i].t; hmr[i].q = INF+hmr[i].s; hmr[i].vis = 0; } sort(hmr, hmr+N, cmp); for (i = 0; i < N; ) { j = i+1; while (j<N && hmr[j].q <= hmr[i].q) hmr[j++].vis = 1; i = j; } j = 0; for (i = 0; i < N; ++i) if (!hmr[i].vis) mdk[++j] = hmr[i]; N = j; sort(mdk+1, mdk+N+1); mdk[0] = ss(-2, -1); for (i=0,j=1; j<N; ++i,j<<=1); int bmax = i; k = 1; for (i = 1; i <= N; ++i) { while (mdk[k].t<=mdk[i].s) k++; prev[i][0] = k-1; } prev[0][0] = 0; for (j = 1; j <= bmax; ++j) { prev[0][j] = 0; for (i = 1; i <= N; ++i) { prev[i][j] = prev[prev[i][j-1]][j-1]; } } for (i = 0; i < M; ++i) { scanf("%d%d", &q[i].s, &q[i].t); q[i].id = i; } sort(q, q+M); k = 0; int b; for (i = 0; i < M; ++i) { while (k<=N && mdk[k].t <= q[i].t) k++; int &res = ans[q[i].id], now = k-1; res = 0; for (j=bmax; j>=0; j--) { if (mdk[prev[now][j]].s>=q[i].s) { res |= (1<<j); now = prev[now][j]; } } if (mdk[k-1].s >= q[i].s) res++; } for (i = 0; i < M; ++i) printf("%d\n", ans[i]); } return 0;}
题目大意:求区间最多有多少不相交线段。
题目思路:先用倍增思想求出dp[i][j]表示左端点为j的线段个数为1<<i的右端点,再由答案的二进制进行搜索。
using namespace std;#define inf 0x3f3f3f3f#define Max 110000int max(int a,int b){ return a>b?a:b;}int min(int a,int b){ return a<b?a:b;}int dp[22][4*Max];struct node{ int v,id; bool operator<(const node a)const { return v<a.v; }}p[4*Max];struct nn{ int l,r; bool operator<(const nn a)const { if(l==a.l) return r>a.r; return l<a.l; }}in[10*Max];int query(int x,int y){ int ans=0; int i; for(i=20;i>=0;i--) { if(dp[i][x]<=y) { x=dp[i][x]; ans+=(1<<i); } } return ans;}int main(){ int cnt,i,j,n,m; while(scanf("%d%d",&n,&m)!=EOF) { cnt=0; for(i=0;i<n+m;i++) { scanf("%d%d",&in[i].l,&in[i].r); p[cnt].id=cnt; p[cnt++].v=in[i].l; p[cnt].id=cnt; p[cnt++].v=in[i].r; } sort(p,p+cnt); int change=0; if(p[0].id%2==0) in[p[0].id/2].l=0; else in[p[0].id/2].r=0; for(i=1;i<cnt;i++) { if(p[i].v!=p[i-1].v) change++; if(p[i].id%2==0) in[p[i].id/2].l=change; else in[p[i].id/2].r=change; } sort(in,in+n); cnt=1; for(i=1;i<n;i++) { while(in[i].r<=in[cnt-1].r) cnt--; in[cnt++]=in[i]; } j=0; for(i=0;i<=change;i++) { for(;j<cnt;j++) { if(in[j].l>=i) break; } if(j<cnt) dp[0][i]=in[j].r; else dp[0][i]=inf; } for(i=1;i<=20;i++) for(j=0;j<=change;j++) { if(dp[i-1][j]==inf) dp[i][j]=inf; else dp[i][j]=dp[i-1][dp[i-1][j]]; } for(i=n;i<n+m;i++) { int ans=query(in[i].l,in[i].r); printf("%d\n",ans); } }}
- hdu - 4343 - Interval query - dp(贪心)
- HDU 4343 Interval query(倍增思想+贪心)
- HDU 4343 Interval query 倍增思想, DP
- hdu 4343 Interval query
- HDU 4343 Interval query
- HDU 4343 Interval query
- hdu 4343 Interval query 【多校5】【数据结构】
- hdu4343 Interval query【贪心+倍增】
- HDU 5489 Removed Interval(DP)
- HDU 4343 Interval query 离散化+倍增思想
- hdu 4655 Cut Pieces(DP 贪心)
- HDU 5303 Delicious Apples(DP + 贪心)
- HDU 5903 - Square Distance (贪心+dp)
- hdu 3466 Proud Merchants(贪心+DP)
- hdu 1257 dp+贪心
- HDU 1257 DP + 贪心
- HDU 2577 DP || 贪心
- hdu 1257--dp/贪心
- 面向切面(AOP)"与"面向对象(OOP)
- linux内核栈,内核同步,用户空间线程同步
- Android TextView的各种属性
- C#时间转JS时间格式
- Android View Animation
- hdu - 4343 - Interval query - dp(贪心)
- 简单FTP(vsftp)架设
- 苹果默认字体
- 1000以内所有偶数和!
- androidx线程详解
- MFC使用动态库dll
- KGDB调试内核
- 为什么很多看起来不是很复杂的网站需要大量顶尖高手来开发
- winform 简易爬网页