直接上题:

本题就是求在m的情况下,那些点能通过吃其他的点,强化自己然后留到最后(挺像大鱼吃小鱼的)。

没错,这就是挂我暴力的题,本来50分的暴力,因为没开longl long只剩可怜的十几分,算了,长个教训吧!

我们刚开始的思想就暴力呗!暴力检查每个点能否扩到最后,然后输出!

显然非正解...

显然最大的点一定是必胜点,那我们再深入思考,必胜点都有什么性质:假如a是必胜点,而b再加强一部分后能吃掉a,那b显然也一定是必胜点!

之后我们再考虑一个点x可以没有m限制的区间,那就是左右两边第一个比m大的数。

没有m,x依然可以在这个区间里随便吃,知道碰到第一个比他大的数,需要验证了。

如果设想p,q分别为第一个比m大的数,如果p是必胜点,而x能吃掉p,那显然x也是必胜点;

可如果p不是必胜点呢?x也一定不是必胜点,为什么呢?因为p比x大而检查p时也一定加上x的那个区间,但p不是必胜点,则x也一定不是必胜点了。

这样一个数的判断依赖于左右两个点的判断,即想知道x,必须知道p,q;这样搞一个记忆化搜索,就可以O(n)求解了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=8100000;
ll n,m,a[N],vis[N],l[N],r[N],top,maxx,sum[N];
struct zhan
{
    ll s,id;
};
zhan q[N];
inline ll read()
{
    ll x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)){if(x=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void put(ll x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) put(x/10);
    putchar(x%10+'0');
}
inline void dfs(int x)
{
    ll ans=sum[r[x]-1]-sum[l[x]];
    if(l[x]!=0&&ans+m>=a[l[x]])
    {
        if(!vis[l[x]]) dfs(l[x]);
        if(vis[l[x]]==1) vis[x]=1;
    }
    if(r[x]!=n+1&&ans+m>=a[r[x]])
    {
        if(!vis[r[x]]) dfs(r[x]);
        if(vis[r[x]]==1) vis[x]=1;
    }
    if(!vis[x]) vis[x]=-1;
    return ;
}
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++) a[i]=read(),maxx=max(maxx,a[i]),sum[i]=sum[i-1]+a[i];
    a[0]=a[n+1]=1e18;
    q[++top].s=a[0];q[top].id=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==maxx) vis[i]=1;
        while(top&&a[i]>=q[top].s) top--;
        l[i]=q[top].id;
        q[++top].s=a[i];q[top].id=i;
    }
    top=0;q[++top].s=a[n+1];q[top].id=n+1;
    for(int i=n;i>=1;i--)
    {
        while(top&&a[i]>=q[top].s) top--;
        r[i]=q[top].id;
        q[++top].s=a[i];q[top].id=i;
    }
    for(int i=1;i<=n;i++) if(!vis[i]) dfs(i);
    for(int i=1;i<=n;i++) if(vis[i]==1) put(i),putchar(' ');
    return 0;
} 
01-18 14:08