题解:P9893 [ICPC 2018 Qingdao R] Soldier Game

借鉴 CF1919F2 的思想。

我们尝试固定最小值,让最大值最小。

容易发现可能更新答案的最小值只会有 \(2n-1\) 个。

直接对 \(a\) 建线段树,对于每个节点维护 \(dp[p][1/0][1/0]\) 表示 \(p\) 节点所管辖的区间 \([l,r]\)\(l\) \([\)\(/\)\(]\)\(l-1\) 在同一段,\(r\) \([\)\(/\)\(]\)\(r+1\) 在同一段。

初始化:

\(a[0]=a[n+1]=inf\)

叶子节点:

dp[p][0][0]=a[l]
dp[p][0][1]=a[l]+a[l+1]
dp[p][1][0]=a[l-1]+a[l]
dp[p][1][1]=inf

非叶子节点直接 pushup,固定左右两端点状态,枚举中间状态,取最小值。

void pushup(int p)
{
    dp[p][0][0]=min({max(dp[lp][0][0],dp[rp][0][0]),max(dp[lp][0][1],dp[rp][1][0])});
    dp[p][0][1]=min({max(dp[lp][0][0],dp[rp][0][1]),max(dp[lp][0][1],dp[rp][1][1])});
    dp[p][1][0]=min({max(dp[lp][1][0],dp[rp][0][0]),max(dp[lp][1][1],dp[rp][1][0])});
    dp[p][1][1]=min({max(dp[lp][1][0],dp[rp][0][1]),max(dp[lp][1][1],dp[rp][1][1])});
}

每次固定最小值 \(x\),调用 \(dp[rt][0][0]\) 即可得到最小的最大值。

然后做所有 \(a[i]=x\)\(i\) 的单点修改和 \(a[i]+a[i-1]=x\)\(i\)\(i-1\) 的单点修改。

实现时将线段树上叶子 \(p\)(管辖 \([i,i]\))的 \(dp[p][0][0]\) 设为 \(inf\) 即可。

还需要判断若 \(a[i-1]+a[i]<=x\),则令 \(dp[p][1][0]=inf\);若 \(a[i]+a[i+1]<=x\),则令 \(dp[p][0][1]=inf\)。表示不能这么选。

发现我们只会修改最多 \(n+2(n-1)\) 次。复杂度大常数 \(O(n \log n)\),可以通过。

注意我们需要将最小值去重,否则可以被卡到 \(O(n^2 \log n)\)

Code:

#include<bits/stdc++.h>
#define int long long

using namespace std;

inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n;
int a[1<<20];
int b[1<<20];

const int N=2e5+5,inf=1e18+1;
vector<int> v[N<<1];
vector<int> ans;
unordered_map<int,int> id;

#define lp (p<<1)
#define rp ((p<<1)|1)

int dp[N<<2][2][2];

void pushup(int p)
{
    dp[p][0][0]=min({max(dp[lp][0][0],dp[rp][0][0]),max(dp[lp][0][1],dp[rp][1][0])});
    dp[p][0][1]=min({max(dp[lp][0][0],dp[rp][0][1]),max(dp[lp][0][1],dp[rp][1][1])});
    dp[p][1][0]=min({max(dp[lp][1][0],dp[rp][0][0]),max(dp[lp][1][1],dp[rp][1][0])});
    dp[p][1][1]=min({max(dp[lp][1][0],dp[rp][0][1]),max(dp[lp][1][1],dp[rp][1][1])});
}

void build(int l,int r,int p)
{
    memset(dp[p],0,sizeof(dp[p]));
    if(l==r)
    {
        dp[p][0][0]=a[l];
        dp[p][0][1]=(l==n?inf:(a[l]+a[l+1]));
        dp[p][1][0]=(l==1?inf:(a[l]+a[l-1]));
        dp[p][1][1]=inf;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,lp);
    build(mid+1,r,rp);

    pushup(p);
}

bool vis[N];

void change(int l,int r,int x,int minn,int p)
{
    if(l==r)
    {
        if(a[l]<=minn) dp[p][0][0]=inf;
        if(l>1&&a[l]+a[l-1]<=minn) dp[p][1][0]=inf;//,change(1,n,l-1,minn,1);
        if(l<n&&a[l]+a[l+1]<=minn) dp[p][0][1]=inf;//,change(1,n,l+1,minn,1);
        
        if(dp[p][0][0]==dp[p][1][0]&&dp[p][1][0]==dp[p][0][1]&&dp[p][0][0]==inf) vis[p]=1;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) change(l,mid,x,minn,lp);
    else change(mid+1,r,x,minn,rp);

    pushup(p);
}

void Deb(int l,int r,int p)
{
    cout<<"["<<l<<","<<r<<"]"<<" p="<<p<<"\n";
    cout<<"dp[p][0][0]="<<dp[p][0][0]<<"\n";
    cout<<"dp[p][0][1]="<<dp[p][0][1]<<"\n";
    cout<<"dp[p][1][0]="<<dp[p][1][0]<<"\n";
    cout<<"dp[p][1][1]="<<dp[p][1][1]<<"\n\n";

    int mid=(l+r)>>1;
    if(l==r) return;
    Deb(l,mid,lp);
    Deb(mid+1,r,rp);
}

void solve(int T)
{
    for(int i=1;i<=n;i++) vis[i]=0;
    id.clear();
    ans.clear();

    n=read();

    a[0]=-inf;
    a[n+1]=inf;

    for(int i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
    sort(b+1,b+1+n);
    b[0]=-inf;
    b[n+1]=inf;
    int tot=0;

    for(int i=1;i<=n;i++)
        if(b[i]!=b[i-1]) id[b[i]]=++tot,ans.push_back(b[i]);

    for(int i=1;i<=n;i++)
        v[id[a[i]]].push_back(i);
    
    for(int i=2;i<=n;i++) if(!id[a[i-1]+a[i]]) id[a[i-1]+a[i]]=++tot,ans.push_back(a[i-1]+a[i]);
    sort(ans.begin(),ans.end());


    int totnum=tot;
    
    for(int i=2;i<=n;i++)
    {
        v[id[a[i]+a[i-1]]].push_back(i-1);
        v[id[a[i]+a[i-1]]].push_back(i);
    }

    int ans_min=inf;
    build(1,n,1);
    for(int i=1;i<=n;i++) vis[i]=0;

    for(int i:ans)
    {
        int nw=id[i];
        if(dp[1][0][0]>=inf) break;
        ans_min=min(ans_min,dp[1][0][0]-i);
        for(int pos:v[nw]) if(!vis[pos]) change(1,n,pos,i,1);
    }

    for(int i=0;i<=tot;i++) v[i].clear();
    ans.clear();

    cout<<ans_min<<"\n";
}

signed main()
{
    int T=read();
    for(int i=1;i<=T;i++)solve(i);

	return 0;
}
posted @ 2025-10-15 20:33  Wy_x  阅读(3)  评论(0)    收藏  举报