题意:在一条数轴上,Alice可以跳到在你所在点前面且值比当前所在点小的点。每回合可以向任意符合要求的点跳一次。当轮到Alice的回合同时不存在符合要求的点,Alice就赢了。Alice可以选择一个点作为起始点,然后作为后手(赛时这里把我坑了)。问有多少个点是必胜的点。 \(n\leq 3\times 10^5\)
首先看一眼 \(n\) 的范围,基本可以猜出复杂度为 \(O(n\log n)\) 或 \(O(n\sqrt n)\) 的。
题目要求有几个必胜的点,可以枚举每个点,再以 \(O(\log n)\) 或 \(O(\sqrt n)\) 的时间解决。
怎么知道一个点是必胜还是必负呢?分两种情况讨论。
一、这个点不能再向其他点跳
显然,若Alice选择了这个点,那Bob就是赢家,这对Alice来说就是必负的点。
二、这个点可以跳向数个知道结果的点
无论是Alice还是Bob,再轮到时都想让其变为一个必胜点,怎么做到呢,将可以跳的必负点留给对手。而前面可以跳的点都是必胜点,那么这个点也只能必负了。也就是说,只要前面有一个必负点,那这个点就必胜,
接下来就能解决问题了:
对于一,我们可以用前缀最小值来知道这是不是该种情况的点。
对于二,我们可以将必胜标记为0,必负标记为1,若可以跳的点的标记之和不为0,即是必胜点,否则是必负点。可以用权值线段树或树状数组,从前往后处理,当一个点必负时,将其加入线段树或树状数组。注意,一中的必负点也需要加入线段树或树状数组中。
并不喜闻乐见的代码时间
点击查看代码
#include<bits/stdc++.h>
#define fo(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define Ts template<typename Ty,typename... Ar>
#define Tp template<typename Ty>
#define ll long long
#define RS register
#define gc getchar
#define pc putchar
#define I inline
using namespace std;
Tp I Ty wmax(Ty a,Ty b){return a>=b? a:b;}
Tp I Ty wmin(Ty a,Ty b){return a<=b? a:b;}
namespace WrongIO
{
Tp I void read(Ty &x){x=0;Ty opt=1;char c=gc();while(!isdigit(c)&&c!='-')c=gc();if(c=='-')opt=-1,c=gc();while(isdigit(c))x=(x<<3)+(x<<1),x+=c-'0',c=gc();x*=opt;return;}
Tp I void write(Ty x){short OI_USE[50],OI_top=0;if(x<=0) if(x==0)pc('0');else pc('-'),x*=-1;while(x)OI_USE[++OI_top]=x%10,x/=10;while(OI_top--)pc(OI_USE[OI_top+1]+'0');return;}
I void writec(char c[]){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);}
I void writes(string s){int len=s.length();for(int i=0;i<len;i++)pc(s[i]);}
I void readc(char &c,int l,int r){c=gc(); while(c!=EOF&&(c<l||c>r)) c=gc();}
I void readc(char &c,char val){c=gc();while(c!=EOF&&c!=val) c=gc();}
I void readc(char val){char c;c=gc();while(c!=EOF&&c!=val) c=gc();}
I void readls(string &s){char c=gc();while(c!='\n') s.push_back(c),c=gc();}
Ts I void read(Ty &x,Ar &...y) {read(x),read(y...);}
} using namespace WrongIO;
ll T,n;
ll st[300050];
ll lowbit(ll x)
{
return x&-x;
}
void add(ll x)
{
for(;x<=n;x+=lowbit(x))
st[x]+=1;
}
ll que(ll x)
{
ll sum=0;
for(;x;x-=lowbit(x))
sum+=st[x];
return sum;
}
ll e[300050];
ll minx[300050];
int main()
{
read(T);
while(T--)
{
memset(st,0,sizeof(st));
memset(minx,0x3f,sizeof(minx));
ll ans=0; read(n);
for(int i=1;i<=n;i++) read(e[i]),minx[i]=wmin(e[i],minx[i-1]);
for(int i=1;i<=n;i++)
{
if(que(e[i])>0||e[i]==minx[i]) ans++;
else add(e[i]);
}
write(n-ans),pc('\n');
}
return 0;
}
//好不容易CF上了青,结果洛谷被JC了,大号没了(悲)