Bzoj3441 乌鸦喝水
Submit: 258 Solved: 97
Description
【题目背景】
一只乌鸦在自娱自乐,它在面前放了n个有魔力的水缸,水缸里装有无限的水。
【题目描述】
他准备从第1个水缸飞到第n个水缸,共m次。在飞过一个水缸的过程中,如果他能够得着水缸里的水,即水缸口到水面距离小于等于乌鸦能够得着的深度,那它就会喝水缸里的水。每喝一次水,所有水缸里的水位都会下降,第i个水缸里的水位会下降Ai,注意喝水是瞬间的,如果乌鸦刚好够得着,但喝完之后够不着,也视为喝到一次,水位也会相应的下降。
Input
共有3行。第一行有三个正整数n、m和x,用空格隔开。n表示水缸的数量,m表示乌鸦飞的次数,x表示乌鸦能够得着的深度。第二行,有n个用空格隔开的正整数,第i个数为第i个水缸中水缸口到水面的距离Wi。第三行,有n个用空格隔开的正整数,第i个为Ai。
Output
只有一行,这一行只有一个正整数,为这只乌鸦能喝到水的次数。
Sample Input
5 2 20
15 14 13 12 12
1 1 1 1 1
15 14 13 12 12
1 1 1 1 1
Sample Output
9
【数据范围】
100%的数据,0<n≤100000,0<m≤100000,0<x≤2000000000,0<Wi≤2000000000,0<Ai≤200。
【数据范围】
100%的数据,0<n≤100000,0<m≤100000,0<x≤2000000000,0<Wi≤2000000000,0<Ai≤200。
HINT
2016.7.8重设时限,未重测!
Source
脑洞题
应该算贪心+二分吧,思路挺神奇的
乌鸦喝水是“能喝的时候一定喝”的,所以和DP决策没关系。
直接模拟的复杂度为$O(nm)$
把每个水缸按照可以喝水的次数(即喝多少次水,水位下降到喝不到)由小到大排序,依次处理。
显然如果前面的水缸能喝n次,后面的水缸至少也能喝n次,这个性质十分有用。
由上面这句话可以推出:如果乌鸦从当前位置飞到队伍末会经过k个能喝的水缸,而当前水缸还能喝的次数x>=k,那么之后的水缸也不会在这一轮被喝空。
1、枚举每一个水缸开始(在枚举到该水缸的时候,前面剩余次数更小的水缸已经被喝完。或者可以理解为,舍弃前面的缸,贪心喝这个。)
2、用树状数组维护水缸的ID,查询从当前水缸的ID到(原先的)第n个水缸还有多少个能喝的水缸,如果当前缸的剩余次数>=剩余缸数,直接更新次数并跳到下一轮。
3、在数次2操作后,当前缸的剩余次数<=剩余缸数,此时在树状数组上二分“余数”,即喝到又一轮的某位置时,当前缸没水了。
4、将当前缸标为空,枚举下一个水缸。
表面看着复杂度高,实际上喝水的次数不会超过最多的那个缸能喝的次数(因为每次喝水都是所有缸水位一起下降的),同时,模拟操作的次数不会超过m(最多喝m轮),复杂度可以接受。
1 /*by SilverN*/ 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<vector> 8 using namespace std; 9 const int mxn=100010; 10 int read(){ 11 int x=0,f=1;char ch=getchar(); 12 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 13 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 14 return x*f; 15 } 16 int n,m,X,N; 17 int t[mxn]; 18 void add(int x,int v){ 19 while(x<=N){t[x]+=v; x+=x&-x;}return; 20 } 21 int que(int x){ 22 int res=0; 23 while(x){res+=t[x];x-=x&-x;} 24 return res; 25 } 26 struct node{ 27 int cnt,id; 28 bool operator < (node b)const{return cnt<b.cnt;} 29 }a[mxn]; 30 int cir,last=0; 31 int tot=0; 32 int find(int x){ 33 int res=last,l=last,r=n; 34 while(l<=r){ 35 int mid=(l+r)>>1; 36 if(que(mid)-que(last)<=x){ 37 res=mid; 38 l=mid+1; 39 } 40 else r=mid-1; 41 } 42 return res; 43 } 44 void solve(){ 45 for(int i=1;i<=n;i++){ 46 if(a[i].cnt<tot){ 47 add(a[i].id,-1); 48 continue; 49 } 50 while(cir<m && tot+(que(n)-que(last))<=a[i].cnt){ 51 int tmp=que(n)-que(last); 52 cir++;//又能喝一轮 53 tot+=tmp; 54 last=0;//从头开始 55 } 56 if(cir>=m)break; 57 int ed=find(a[i].cnt-tot); 58 // tot+=ed-last; //wrong 59 tot=a[i].cnt; 60 last=ed; 61 add(a[i].id,-1); 62 } 63 return; 64 } 65 int main(){ 66 int i,j,w; 67 N=n=read();m=read();X=read(); 68 for(i=1;i<=n;i++) 69 a[i].cnt=read(),a[i].id=i; 70 for(i=1;i<=n;i++){ 71 w=read(); 72 a[i].cnt=(X-a[i].cnt)/w+1; 73 } 74 w=0; 75 for(i=1;i<=n;i++) 76 if(a[i].cnt){ 77 a[++w]=a[i]; 78 add(a[i].id,1); 79 } 80 n=w; 81 sort(a+1,a+n+1); 82 solve(); 83 printf("%d\n",tot); 84 return 0; 85 }
本文为博主原创文章,转载请注明出处。