• 【CS Round #46 (Div. 1.5) E】Ultimate Orbs


    【链接】链接


    【题意】


    n个人从左到右站在一条直线上。每个人都有一个能力值g[i],然后每个人可以将相邻的一个人打败。
    然后它的能力值能够增加相应的能力值(就是打败了的那个人的能力值).
    A能够打败B的条件是g[A]+D>=g[B].
    n-1次后,只会剩下一个人了。
    问你最后的那一个人可能是谁。
    输出所有可能的人的编号。

    【题解】


    设dfs(i),表示要找第i个人能不能成为最后的winner.
    暴力做的话,肯定会超时的。
    ->O(N^2)的算法了。
    于是我们寻求更快的方法。
    我们维护以下两个东西,
    l[i]和r[i].
    l[i]表示的是最大的jj<i,且g[j]>g[i]的下标j.(如果没有就为0)
    r[i]表示的是最小的jj>i,且g[j]>g[i]的下标j.(如果没有就为N)
    这两个东西,能用一个单调递减的单调队列弄出来。
    O(N)就能得到。
    然后考虑某一个位置i;
    我们想要获取第i个位置是不是可以成为最后的Winner.
    设这个问题为dfs(i);
    显然l[i]+1..r[i]-1这一段都是g[i]能够不用增加D就吃到的
    那么。我们把i这个人的能力值tot加上sum[l[i]+1..r[i]-1] (前缀和就能搞出来);
    接下来,看看,这个tot+D能不能大于等于g[ l[i] ];
    如果可以的话,则这个问题就能转化为,求dfs(l[i])了.
    也即,转化为l[i]能不能成为winner了
    因为对于l[i]而言,l[i]+1..r[i]-1这一段,它是肯定能够吃掉的。
    (因为g[ l[i]+1..r[i]-1 ]是比g[i]小的,而g[ l[i] ] > g[i])
    那么也就说,从i点吃到r[i]-1点,再一直吃到l[i]点..
    再想一下,如果我们在做dfs(l[i]),则肯定也会把g[ l[i]+1..r[i]-1 ]全都吃掉.
    不就是等价的了吗?
    也就是说,现在问题已经完全转化为l[i]这个点能不能成为最后的winner了
    对于dfs(l[i]),还是一样的,在l[ l[i] ]+1..r[ l[i] ] -1之间的人,都会被吃掉。然后又+D看看能不能再往外扩展
    可以的话,就又能转化为dfs( l[ l[i] ] -1)或dfs(r[ l[i] ] + 1)这两个问题了
    直到l[i]==0且r[i]==N,则可以直接返回true.因为这是个最大的数了,肯定能吃掉所有的.
    (且可以肯定,dfs(x)中的x是一直在增大的)
    当然还可以试一下r[i].(如果tot+D>=g[ r[i] ]就可以再试一下r[i]);
    因为同理也能从l[i]+1吃到r[i],然后转化为dfs(r[i])这个问题
    只要l[i]和r[i]中的任意一个可以成为winner.
    那么i就能成为winner.
    且,因为l[i]的区间l[ l[i] ]..r[ l[i] ]区间肯定覆盖了l[i]..r[i]区间。所以不会出现环
    考虑到上面的过程是一个递归的过程
    且找过一次答案之后,答案就是确定的了。
    有大量重复的子问题。
    写一个记忆化搜索就可以了。
    这是一个类似DAG的问题

    UPD
    l[i]和r[i]是i这个人必须要吃掉的人(因为都比它大),则我们必须要吃掉其中一个人才能继续吃
    (这是必须要做的事情,必须这么贪心,算法的正确性没有问题);
    一旦能够吃l[i]或r[i],会发现,问题能转化成求另外一个位置的问题,(只有那个位置可以,i的位置才可以,否则不行!)
    那个问题,还是用相同的方法去做,即新的i,然后又去打败li或ri(必须打败),然后打败之后,有能转化为另外一个位置的问题,且会发现,递归的过程不会出现环,即dfs(x)->dfs(y),然后dfs(y)里面又要调用dfs(x)这样的情况,不会出现。因为
    g[y]>g[x]总是成立的,所以我们调用的dfs(x)的g[x]是单调递增的,总会有出口的(l[i]..r[i]这个区间会越来越大,直到变成
    [0..N]这个区间,也即x变成了最大的数字)

    【错的次数】


    0

    【反思】


    出现了重复的问题->考虑记忆化搜索
    考虑动规。
    重复的子问题。

    【代码】

    /*
    
    
    */
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <set>
    #include <cstdlib>
    #include <cmath>
    #include <bitset>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define LL long long
    #define rep1(i,a,b) for (int i = a;i <= b;i++)
    #define rep2(i,a,b) for (int i = a;i >= b;i--)
    #define mp make_pair
    #define pb emplace_back
    #define fi first
    #define se second
    #define ld long double
    #define ms(x,y) memset(x,y,sizeof x)
    #define ri(x) scanf("%d",&x)
    #define rl(x) scanf("%lld",&x)
    #define rs(x) scanf("%s",x)
    #define rf(x) scnaf("%lf",&x)
    #define oi(x) printf("%d",x)
    #define ol(x) printf("%lld",x)
    #define oc putchar(' ')
    #define os(x) printf(x)
    #define all(x) x.begin(),x.end()
    #define Open() freopen("F:\rush.txt","r",stdin)
    #define Close() ios::sync_with_stdio(0)
    #define sz(x) ((int) x.size())
    #define ld long double
    
    
    typedef pair<int,int> pii;
    typedef pair<LL,LL> pll;
    
    
    //mt19937 myrand(time(0));
    //int get_rand(int n){return myrand()%n + 1;}
    const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
    const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
    const double pi = acos(-1.0);
    const int N = 1e6;
    
    
    int n,d,l[N+10],r[N+10],dp[N+10];
    LL sum[N+10];
    pll a[N+10];
    vector <pll> v;
    vector <int> ans;
    
    
    int dfs(int x){
        if (dp[x]!=-1) return dp[x];
        if (l[x]==-1 && r[x]==-1){
            return dp[x] = true;
        }
        LL temp = a[x].fi;
        if (l[x]==-1)
            temp += sum[x-1];
        else
            temp += sum[x-1] - sum[l[x]];
        if (r[x]==-1)
            temp += sum[n] - sum[x];
        else
            temp += sum[r[x]-1] - sum[x];
        if (l[x]!=-1 && temp + d >= a[l[x]].fi && dfs(l[x]))
            return dp[x] = true;
        if (r[x]!=-1 && temp + d >= a[r[x]].fi && dfs(r[x]))
            return dp[x] = true;
        return dp[x] = false;
    }
    
    
    int main(){
        ms(dp,255);
        //Open();
        //Close();
        ri(n),ri(d);
        rep1(i,1,n){
            LL x;
            rl(x);
            a[i].fi = x,a[i].se = i;
            sum[i] = sum[i-1] + x;
        }
    
    
        rep1(i,1,n){
            while (!v.empty() && v.back().fi<=a[i].fi){
                    v.pop_back();
                }
            //v.back() > a[i].fi
            if (v.empty()){
                l[i] = -1;
            }else{
                l[i] = v.back().se;
            }
            v.pb(a[i]);
        }
    
    
        v.clear();
        rep2(i,n,1){
            while (!v.empty() && v.back().fi<=a[i].fi){
                    v.pop_back();
                }
            //v.back() > a[i].fi
            if (v.empty()){
                r[i] = -1;
            }else{
                r[i] = v.back().se;
            }
            v.pb(a[i]);
        }
    
    
        rep1(i,1,n)
            if (dfs(i)){
                ans.pb(i);
            }
        rep1(i,0,sz(ans)-1){
            oi(ans[i]);
            if (i==sz(ans)-1)
                puts("");
            else
                oc;
        }
        return 0;
    }


  • 相关阅读:
    Easyui datagrid 修改分页组件的分页提示信息为中文
    Easyui datagrid 实现表格记录拖拽
    Java:内部类
    算法导论:Trie字典树
    算法导论:找零钱问题
    lintcode:组成最大的数
    lintcode:验证二叉查找树
    lintcode:将二叉查找树转换成双链表
    lintcode:二叉树的路径和
    lintcode:字符串置换
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7626042.html
Copyright © 2020-2023  润新知