Codeforces1257D (贪心+缓存,或者二分+线段树)

题目大意:有n个怪物,每个怪物都有一个能力值a。有m个勇士,每个勇士都有一个力量P和耐力S。明天可以选择一个勇士去打怪物,必须按顺序打,如果这个勇士的能力P>=a那么就可以打败这个怪物,就必须打下一个怪物,而且最多一天只能打S个怪物。如果力量P<a。这个勇士就回来。这天结束,每个勇士可以使用无数次。问最少需要多少天,能把所有的怪物全部打败。

 

给的数据范围分别是  n(1<=n<=2e5),  m(1<=m<=2e5)     (1<=pi<=1e9,1<=si<=n)    所有询问的n+m加起来不超过2e5

 

这道题一个很正常的思路(或者说我的一般思路是)     首先我们怎么来求解决了前i只怪物的最小时间?????   

那么就可以考虑解决第i个怪物的勇士他所解决的区间的问题,这样的操作可以二分解决,然后在用线段树维护?????

但是感觉这样有点难写?而且很容易wa,我就wa了不知道多少遍。

下面是比赛时写的代码:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <bitset>
  7 #include <set>
  8 typedef long long ll;
  9 using namespace std;
 10 const int maxn=201000;
 11 int t,n,m;
 12 int num[maxn],sum1[maxn<<2],sum2[maxn<<2],dp[maxn],q[maxn],p[maxn],s[maxn];
 13 struct node{
 14     int pi,si;
 15 };
 16 node fnum[maxn];
 17  
 18 bool cmp(node n1,node n2){
 19     if(n1.pi==n2.pi) return n1.si<n2.si;
 20     else return  n1.pi<n2.pi;
 21 }
 22  
 23 void build1(int l,int r,int rt){
 24     if(l==r){
 25         sum1[rt]=num[l];
 26         return;
 27     }
 28     int mid=(l+r)/2;
 29     build1(l,mid,rt<<1);
 30     build1(mid+1,r,rt<<1|1);
 31     sum1[rt]=max(sum1[rt<<1],sum1[rt<<1|1]);
 32 }
 33  
 34 int ask(int l,int r,int rt,int L,int R){
 35     if(L<=l&&r<=R) return sum1[rt];
 36     int mid=(l+r)/2;
 37     int ans=0;
 38     if(L<=mid) ans=max(ans,ask(l,mid,rt<<1,L,R));
 39     if(R>mid) ans=max(ans,ask(mid+1,r,rt<<1|1,L,R));
 40     return ans;
 41 }
 42  
 43 void adds(int l,int r,int rt,int k,int k1){
 44     if(l==r){
 45         sum2[rt]=k1;
 46         return;
 47     }
 48     int mid=(l+r)/2;
 49     if(k<=mid) adds(l,mid,rt<<1,k,k1);
 50     else  adds(mid+1,r,rt<<1|1,k,k1);
 51     sum2[rt]=min(sum2[rt<<1|1],sum2[rt<<1]);
 52 }
 53  
 54 int Min(int l,int r,int rt,int L,int R){
 55     if(L<=l&&r<=R) return sum2[rt];
 56     int mid=(l+r)/2;
 57     int ans=(int)(1e9+7);
 58     if(L<=mid) ans=min(ans,Min(l,mid,rt<<1,L,R));
 59     if(R>mid) ans=min(ans,Min(mid+1,r,rt<<1|1,L,R));
 60     return ans;
 61 }
 62  
 63  
 64 bool check(int l,int  r){
 65     int q1=ask(1,n,1,l,r);
 66     int iscan=(int)(lower_bound(p+1,p+m+1,q1)-p);
 67     //if(l==1&&r==2) printf("%d  %d  %d %d\n",q1,num[1],num[2],iscan);
 68     if(iscan>=1&&iscan<=m&&q[iscan]>=(r-l+1)) return true;
 69     return false;
 70 }
 71  
 72 int main(){
 73     scanf("%d",&t);
 74     while(t--){
 75         scanf("%d",&n);
 76         for(int i=1;i<=n;i++) scanf("%d",&num[i]);
 77         build1(1,n,1);
 78         scanf("%d",&m);
 79         for(int i=1;i<=m;i++) scanf("%d%d",&fnum[i].pi,&fnum[i].si);
 80         sort(fnum+1,fnum+m+1,cmp);
 81         int flag=0;
 82         for(int i=1;i<=m;i++){
 83             //if(fnum[i].pi<num[i])  flag=1;
 84             p[i]=fnum[i].pi;s[i]=fnum[i].si;
 85         }
 86         for(int i=1;i<=n;i++) if(fnum[m].pi<num[i]) flag=1;
 87         if(flag==1){printf("-1\n");continue;}
 88  
 89         q[m]=fnum[m].si;
 90         for(int i=m-1;i>=1;i--)  q[i]=max(q[i+1],s[i]);
 91  
 92         dp[1]=1;dp[0]=0;
 93         adds(0,n,1,1,1);   adds(0,n,1,0,0);
 94         for(int i=2;i<=n;i++){
 95             int l=1,r=i,ans=i;
 96             while(l<=r){
 97                 int mid=(l+r)/2;
 98                 if(check(mid,i)==1){ans=mid;r=mid-1;}
 99                 else l=mid+1;
100             }
101             // cout<<ans-1<<"  "<<i-1<<endl;
102             dp[i]=Min(0,n,1,ans-1,i-1)+1;
103             adds(0,n,1,i,dp[i]);
104         }
105         printf("%d\n",dp[n]);
106     }
107     return 0;
108 }
View Code

 

 

然后就是一个很妙的解法了,我也不知道怎么才能在考试里面想到这样的解法??????

tql!!!

(1):首先我们可以发现这个东西是可以贪心的,就是对于每个勇士他可以杀的怪兽越多越好,总而言之就是能啥多只怪兽就啥多少怪兽,直接贪心下去就ok了

 

(2):然后我们需要抛弃m这个东东,因为没啥用,考虑能连续作战i天的勇士的fi的最大值,这样的话我们如果能坚持i天,就直接撸下去,而不需要考虑那个特定的勇士

 

(3):直接将值域映射到i上面,这样是最好做的

 

下面是代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <iostream>
 5 #include <cmath>
 6 #include <bitset>
 7 typedef long long ll;
 8 using namespace std;
 9 const int maxn=201000;
10 int num[maxn],mx[maxn];
11 int t,n,m;
12 int main(){
13     scanf("%d",&t);
14     while(t--){
15         scanf("%d",&n);
16         for(int i=1;i<=n;i++) scanf("%d",&num[i]);
17         for(int i=1;i<=n;i++) mx[i]=0;
18         scanf("%d",&m);
19         for(int i=1;i<=m;i++){
20             int pi,si;
21             scanf("%d%d",&pi,&si);
22             mx[si]=max(mx[si],pi);
23         }
24         for(int i=n-1;i>=1;i--)  mx[i]=max(mx[i],mx[i+1]);
25 
26         int pos=1,flag=1,day=0;
27         while(pos<=n){
28             day++; int tmp=pos,maxn=0;
29             while(tmp<=n){
30                 maxn=max(maxn,num[tmp]);
31                 if(maxn>mx[tmp-pos+1]) break;
32                 ++tmp;
33             }
34             if(tmp==pos){
35                 flag=0;break;
36             }
37             pos=tmp;
38         }
39         if(!flag) day=-1;
40         printf("%d\n",day);
41     }
42     return 0;
43 }
View Code

posted on 2019-12-13 14:45  pandaking  阅读(226)  评论(0编辑  收藏  举报

导航