2018.10.27 搬砖题解
【问题描述】
我不会告诉你 moreD 神犇最喜欢搬砖了。 砖, 当然是指魔砖, 魔法砖带有一个魔法标记, 只能摆在区间[Li, Ri] 里。 正所谓日 久生情, moreD 对砖头的热爱已经到了一个忘我的境界, 他认为砖 头也是有生命的, 他希望各个砖可以和睦相处。 具体来说是砖头越多越好。 moreD 每一次选定一个区间, 然后选定一些砖头放入。 使得这些砖头互不重 叠, 且任意选定的砖头的魔法标记不超出 moreD 选定的区间。 对于 moreD 每一次选定的区间, 你需要回答 moreD, 他最多可放入多少砖头 哦。
【输入格式】
第一行三个整数 N, Q, len, 表示砖头的数量, 询问数和 len 的大小, 1≤Li, Ri ≤len。 接下来 N 行, 每行两个整数 Li, Ri, 表示砖头的魔法标记。 接下来 Q 行, 每行两个整数 pi, qi, 表示 moreD 选定的区间。
【输出格式】
对于每组询问输出对应的答案。
【输入样例】
3 2 4
1 2
2 3
3 4
1 3
3 3
【输出样例】
10
【数据范围】
对于 30%的数据满足 N≤10, Q≤10
对于 60%的数据满足 N≤1, 000, Q≤1, 000 1≤Li, Ri≤1, 000
对于 100%的数据满足 N≤100, 000, Q≤100, 000 1≤Li, Ri≤len≤100, 000
早上考试时没有想到正解,直接在每一个询问区间去DP,结果就只有60分呢。
本题的满分解法是倍增
考虑这样一个数组 f[i][j] 表示从i这个地方起,取2j段在到达的最右端点的最小值是多少。
显然可以再O(lenlogn)的时间内预处理出这个数组。
接下来针对每一次询问,直接在[l,r]的区间里面倍增就可以了。
具体就是从大到小枚举j, 如果f[l][j]<=r, 就加上2j段,然后再跑到f[l][j]+1~r去倍增即可。
附上代码(我用vector将每个砖头挂在每左端点上)
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<vector> #define For(i,a,b) for(register int i=a;i<=b;++i) #define Dwn(i,a,b) for(register int i=a;i>=b;--i) #define Re register #define Pn putchar('\n') using namespace std; const int N=1e5+10; int f[N][25]; vector<int>brk[N]; int l,r,n,m,x,y,Q,len,tw[25]; inline void read(int &v){ v=0; char c=getchar(); while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar(); } void write(int x){ if(x>9)write(x/10); int xx=x%10; putchar(xx+'0'); } int getA(int lx,int rx){ int px=lx; int ans=0; Dwn(b,20,0){ if(f[px][b]!=-1){ if(f[px][b]>rx)continue; px=f[px][b]+1; ans+=tw[b]; } } return ans; } int main(){ freopen("block.in","r",stdin); freopen("block.out","w",stdout); read(n); read(Q); read(len); For(i,0,20)tw[i]=1<<i; For(i,1,n){ read(l); read(r); brk[l].push_back(r); } memset(f,-1,sizeof(f)); int mn=-1; Dwn(i,len,1){ int Bsz=brk[i].size(); For(j,0,Bsz-1){ if(mn==-1)mn=brk[i][j]; else mn=min(mn,brk[i][j]); } f[i][0]=mn; } For(b,1,20) For(i,1,len){ if(f[i][b-1]==-1)continue; int rx=f[i][b-1]+1; f[i][b]=f[rx][b-1]; } For(i,1,Q){ read(l); read(r); int fn=getA(l,r); write(fn); Pn; } fclose(stdin); fclose(stdout); return 0; }