「CSP2024-擂台游戏」题解

P11234 [CSP-S 2024] 擂台游戏

sol

本篇题解来源于我此前某篇被删除的补题记录所以马蜂与语言风格可能与当前不同。

我们使用“时刻 \(x\)”表示已知前 \(x\) 个人的属性。

我们发现,一个人可能被记录答案的时刻区间,必然是一个时刻前缀,因为任意赋值属性的人越多显然是不劣的。我们可以通过差分数组计算答案。

因此,当某一个时刻之后,这个节点就不会贡献了。我们现在就需要找到这个时刻。

对于一个子树,在一个时刻之后,其内部获胜的节点就是固定的。而在此之前,获胜的人是不固定的,且获胜的人必然是未知属性的节点。我们可以一次深搜得到这两个属性。

你会发现,对于未知属性的节点,他为擂主时,可以任意输赢。因此在一场比赛中,我们只需要考虑擂主属性已知的情况。

接下来我们考虑如何统计每个点的答案。对每个点都跑到根节点然后进行判定是不现实的,我们可以考虑类似线段树那样把限制沿路下传。

对于一场比赛,一个节点可能赢,情况如下:

  • 他是擂主,且属性不固定或大于等于当前场数。
  • 他不是擂主,且擂主属性不固定或小于当前场数。

我们考虑储存当前子树可能赢的时刻区间,不难发现这显然是个连续区间(更准确的,前缀)。

这样传递到最底层的节点,我们将其贡献分成两部分:他属性固定或不固定。

对于他属性固定的时刻,只需要储存他要想赢的最高比赛轮次数,然后与其属性比较即可。他属性不固定,必然可能会赢。

最后,我们发现整棵树的大小是不固定的,考虑枚举树大小。注意每棵树都有一个固定的存在时刻区间,只有在这个区间内才会有这样的树。复杂度仍然是 \(O(n)\) 的。

code

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef double db;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template <typename Type>
using vec=vector<Type>;
template <typename Type>
using grheap=priority_queue<Type>;
template <typename Type>
using lrheap=priority_queue<Type,vector<Type>,greater<Type> >;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define per(i,x,y) for(int i=(x);i>=(y);i--)
#define repl(i,x,y) for(int i=(x);i<(y);i++)
#define file(f) freopen(#f".in","r",stdin);freopen(#f".out","w",stdout);

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7/*998244353*/;

const int N=1e5+5,H=17;

int n,m;
int K;
int A[N],a[(1<<H)+5],c[N];
int d[(1<<H)+5];
int X[4];
int t[1<<H<<1],f[1<<H<<1];
ll cf[(1<<H)+5];

void init(int now){
    int r=K-__lg(now);
    if(!r){
        t[now]=f[now]=now-(1<<K)+1;
        return;
    }
    init(now<<1);init(now<<1|1);
    if(!d[now]&&a[f[now<<1]]>=r)t[now]=t[now<<1];
    else t[now]=t[now<<1|1];
    if(!d[now]&&a[f[now<<1]]>=r||d[now]&&a[f[now<<1|1]]<r)f[now]=f[now<<1];
    else f[now]=f[now<<1|1];
}

void sol(int now,int dh,int l,int r){
    if(l>r)return;
    int h=K-__lg(now);
    if(!h){
        int x=now-(1<<K)+1;
        if(l<x)cf[l]+=x,cf[min(x,r+1)]-=x;
        if(x<=r&&a[x]>=dh)cf[max(x,l)]+=x,cf[r+1]-=x;
        return;
    }
    if(!d[now]){
        sol(now<<1,max(dh,h),l,r);
        sol(now<<1|1,dh,l,min(a[f[now<<1]]>=h?t[now<<1]-1:r,r));
    }else{
        sol(now<<1,dh,l,min(a[f[now<<1|1]]>=h?t[now<<1|1]-1:r,r));
        sol(now<<1|1,max(dh,h),l,r);
    }
}

void solve(){
    rep(i,0,3)cin>>X[i];
    rep(i,1,n)a[i]=A[i]^X[i%4];
    init(1);
    rep(i,1,n)cf[i]=0;
    rep(k,0,K)sol(1<<k,0,(k==K?1:(1<<K-k-1)+1),1<<K-k);
    rep(i,1,n)cf[i]+=cf[i-1];
    ll ans=0;
    rep(i,1,m)ans^=i*cf[c[i]];
    cout<<ans<<"\n";
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
    K=__lg(n-1)+1;
    rep(i,1,1<<K)a[i]=inf;
    rep(i,1,n)cin>>A[i];
    rep(i,1,m)cin>>c[i];
    per(k,K-1,0)repl(i,0,1<<k){
        char ch;cin>>ch;
        d[(1<<k)+i]=ch-'0';
    }
    int t;cin>>t;
    while(t--)solve();
    return 0;
}
posted @ 2025-07-10 21:22  LastKismet  阅读(45)  评论(0)    收藏  举报