直接上题:
本题就是求在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; }