题目链接:http://codeforces.com/contest/1257/problem/D
题目大意:有n个怪兽,对应n个攻击力,m个奥特曼(大雾),每个奥特曼有一个攻击力和攻击天数(他们可以任意派出和使用),怪兽必须按顺序打败,问最少多少个奥特曼可以击败所有怪兽,若击败不了所有怪兽,输出-1

从这篇题解中真的学到很多,写这种思维题的时候,我没有学会用整体的思维去考虑问题,应该像当初写高中数学题时候,学会从题目本身寻找突破口。

李煜东大神教我们,每道题应该分为几个子问题然后一个一个的解决。
从题解上来看,
首先,第一个子问题,非常明显,维护的重点不能放在他的攻击力上,如果对攻击力排序,而优先使用攻击力大的奥特曼,那并不能保证他的攻击天数最多,我们不能选攻击力最大的,应该选最合适的,这就是我没有想到的地方,本来我觉得可能利用攻击力高的去贪心加搜索,显然复杂度是不能够接受的。
At first, lets precalc array bst; bsti is equal to maximum hero power whose endurance is greater than or equal to i.
首先我们应该预处理出bst数组,代表的是能够攻击i天的英雄的最大攻击力是多少
维护天数很明显就可以解决我们按顺序击败怪兽的问题,天数区域内的最大值和当前bst[i]比较便可以O(n)的维护最小攻击天数,真是太精妙了。于是看懂了题解的我迫不及待的上手写代码,然而我是这么维护的

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+9;
int t,n,m;
int a[maxn];
priority_queue<int>q[maxn];
int main()
{
    scanf("%d",&t);
    while(t--){
        for(int i=1;i<=n;i++){
            while(q[i].size())q[i].pop();
        }
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        scanf("%d",&m);
        int ins=-1;
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            for(int j=1;j<=y;j++){
                q[j].push(x);
            }
            ins=max(ins,y);
        }
        int num=1,maxx=-1,ans=1,flag=1;
        for(int i=1;i<=n;i++){
            maxx=max(a[i],maxx);
            if(maxx<=q[num].top()){
                num++;
                if(num>ins&&i!=n){
                    ans++;
                    num=1;
                    maxx=-1;
                }
            }
		 else{
                if(num==1){
                    flag=0;
                    break;
                }
                num=1;maxx=-1;
                if(q[num].top()>=a[i]){
                    num++;maxx=max(a[i],maxx);
                    ans++;
                    if(num>ins){
                        num=1;
                    }
                }
                else{
                    flag=0;break;
                }
            }
        }
        if(flag){
            printf("%d\n\n",ans);
        }
        else{
            printf("-1\n");
        }
    }
    return 0;
}

我竟然不知死活的拿优先队列加n方的复杂度去维护,复杂度n2logn,哈哈。。
于是我瞟了一眼题解,就一小眼,仅仅是那么一个小眼神,我顿时就豁然开朗,仿佛见到了一个桃花源,,,,如果维护每个天数的max,然后再逆序的维护一遍max不就行了嘛!!!!真的是思维本质!能够持续打n天的最大值一定能够打n-1天呐。(这个思维是真的精妙,惊叹.jpg)
最后学到的微不足道的小玩意,少用memset清空数组,不然就会像这样
好了上代码,,,,顺便%%%全神前三百又上分了,等会补一下全神的做法

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+9;
int t,n,m;
int a[maxn];
int q[maxn];
int main()
{
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            q[i]=0;
        }
        scanf("%d",&m);
        int ins=-1;
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            q[y]=max(q[y],x);
            ins=max(ins,y);
        }
        for(int i=ins-1;i>=1;i--){
            q[i]=max(q[i],q[i+1]);
        }
        int num=1,maxx=-1,ans=1,flag=1;
        for(int i=1;i<=n;i++){
            maxx=max(a[i],maxx);
            if(maxx<=q[num]){
                num++;
                if(num>ins&&i!=n){
                    ans++;
                    num=1;
                    maxx=-1;
                }
            }
            else{
                if(num==1){
                    flag=0;
                    break;
                }
                num=1;maxx=-1;
                if(q[num]>=a[i]){
                    num++;maxx=max(a[i],maxx);
                    ans++;
                    if(num>ins){
                        num=1;
                    }
                }
                else{
                    flag=0;break;
                }
            }
        }
        if(flag){
            printf("%d\n",ans);
        }
        else{
            printf("-1\n");
        }
    }
    return 0;
}