HDU 5603 the soldier of love 离线+树状数组
这是bestcorder 67 div1 的1003 当时不会做 看了赛后官方题解,然后翻译了一下就过了,而且速度很快,膜拜官方题解。。
附上官方题解:
the soldier of love
我们注意到我们求的是每一组至少覆盖一个点的线段数量。
那么我们可以先求一个点都没有覆盖的的线段数量,在用nn减去即可。
我们把所有点和线段先这样离线处理:
对于每个线段,在他的右端点处记上一个左端点的标记。
对于每组点,在除了第一个点之外的其他点上,记上一个前一个点的标记,同时记录下这个点的编号。
然后从1到10^6扫一遍数轴,对每个点处理所有标记,先处理点的。
对于每一个点,找到他的前一个点,把树状数组中[p_{now} - 1, p_{pre} + 1][pnow−1,ppre+1]中的和累积到这个点的编号的答案里面。
然而这个树状数组是记录什么的呢,对于每个点,找到他的线段标记,也就是这个线段的左端点,把左端点的位置在树状数组里面累加。
也就是说,对当前这个位置,树状数组记录的都是右端点在当前点左边的所有线段的左端点的累加和。 就这样O((n+m_sum)log10^{6})O((n+msum)log106)的时间复杂度解决。
注:还有一点题解的做法,你记录3e5的点的数组需要开二倍,这一点很重要,我觉得好恶心,因为数组开小了,交到杭电,返回T,让我一度对官方题解产生了怀疑
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmath> #include<cstdlib> #include<vector> #include<queue> using namespace std; typedef long long LL; const int INF=0x3f3f3f3f; const int maxn=1000000+5; const int maxm=300000+5; struct Point { int x,pre,bel; bool operator<(const Point &e)const { return x<e.x; } } o[maxm*2]; struct Seg { int l,r; bool operator<(const Seg &e)const { return r<e.r; } } e[maxm]; int c[maxn]; int lowbit(int x) { return x&(-x); } void add(int i) { while(i<maxn) { ++c[i]; i+=lowbit(i); } } int query(int i) { int sum=0; while(i>0) { sum+=c[i]; i-=lowbit(i); } return sum; } int ans[maxm]; int main() { int n,m; while(~scanf("%d%d",&n,&m)) { memset(c,0,sizeof(c)); memset(ans,0,sizeof(ans)); for(int i=1; i<=n; ++i) scanf("%d%d",&e[i].l,&e[i].r); int cnt=0; for(int i=1; i<=m; ++i) { int k; scanf("%d",&k); for(int j=0; j<k; ++j) { scanf("%d",&o[++cnt].x); o[cnt].bel=i,o[cnt].pre=-1; if(j==0)continue; o[cnt].pre=o[cnt-1].x; } o[++cnt].x=1e6+1; o[cnt].bel=i,o[cnt].pre=o[cnt-1].x;; } sort(e+1,e+1+n); sort(o+1,o+1+cnt); int now=1; for(int i=1; i<=cnt; ++i) { while(now<=n&&e[now].r<o[i].x) { add(e[now].l); now++; } ans[o[i].bel]+=query(o[i].x-1)-query(o[i].pre);; } for(int i=1;i<=m;++i) printf("%d\n",n-ans[i]); } return 0; }