P11234 擂台游戏 题解

· · 题解

题意大概是在一个二叉树状物上跑了一些东西。在这里我们把“作为擂主”的点连向其父亲的边称作“关键边”。

容易知道关键边的数量是 O(n) 的。所以我们可以考虑暴力跳关键边。

但我们还需要观察出一些结论与性质:(在此称“原先的选手”为“A 类”,“补充的选手”为“B 类”)

证明可能比较感性:因为 B 类的能力值是可以随意调整的,所以必然能控制它恰好在某一轮比赛输或者赢。

因为比赛过程确定,选手能力值也确定,所以结果确定。

由前两个性质可以知道,被 A 类打败的 A 类在之后必然失败;被 B 类打败的 A 类只会是守擂输了,在之后也必然失败。

以上性质启发我们:按编号从小到大加入人,在过程中不断将不可能成为冠军的人删掉。

注意:维护的是删除,而非维护可能获胜的人。

具体的,我们考虑对于每个点通过跳关键边的方式来维护这个过程。因为如果一个点的父亲边不是关键边,那么它就不必继续往上跳,所以每个点被访问的次数是 O(1) 的。

以下是跳关键边的过程:(此处定义“被 B 类影响”的点表示 B 类点或者其对应的擂主被 B 类影响的点)

由性质 2,我们知道不被 B 类影响的点有唯一胜者。

我们在每个不被 B 类影响的点存下当前点对应的唯一胜者,这样就可以知道当前点对应的胜者的能力值了。

给出一个结论:对于被 B 类影响的点,我们可以直接退出,等到没有 B 类对这个点有影响时再计算。

因为如果一个点被 B 类影响,则由性质 1 必然有一个可能的胜者是 B 类,从而得出它不影响其它比赛。

那么跳的时候有四种可能:

至此,我们将这题的主体部分做完了,剩下一部分是细节处理。

以上。单组数据跳关键边部分 O(n),预处理部分 O(n),总时间复杂度 O(Tn),理论可过,民间数据可过。

考场代码可读性极差,仅作参考吧

#include<bits/stdc++.h>
//#define int long long 如果我似了全怪这句话
using namespace std;

inline int read(){
    int n,f=1; char c;
    while (!isdigit(c=getchar())) f=(c=='-'?-1:1); n=c-'0';
    while (isdigit(c=getchar())) n=n*10+c-'0';
    return n*f;
}
void write(long long x){
    if (x>=10) write(x/10);
    putchar(x%10+'0');
}
void write_(long long x,char c){
    if (x<0) putchar('-'), x=-x;
    write(x); putchar(c);
}

int n,m,zzp=0;
int a_[270005], zzpx[10]; 
int a[270005], b[270005];
char s[270005];

char tag[270005], tag2[270005];
bool flag[270005];
long long w[270005], mn[270005];

void Init(){
  //预处理,标记关键边
    memset(flag,0,sizeof(flag));
    for (int i=1;i<=n;i++) a[i]=(a_[i]^zzpx[i%4]);
    for (int i=1;i<=(1<<zzp);i++) w[(1<<zzp)+i-1]=i;
    for (int i=zzp-1;i>=0;i--){
        for (int j=1;j<=(1<<i);j++){
            int p=(1<<zzp)-(1<<i+1)+j, id=(1<<i)+j-1;
            if (s[p]=='0') tag2[id]=1, tag[id*2]=1, tag[id*2+1]=0;
            else tag2[id]=2, tag[id*2]=0, tag[id*2+1]=1;
            w[id]=w[id*2]+w[id*2+1];
        }
    }
}

int T1;
long long cur, ans[200005];

void check_mn_zzp(int u,int dep,int mx_){
  //预处理能力值限制
    if (dep==0){
        mn[u]=mx_;
        return ;
    }
    check_mn_zzp((u<<1),dep-1,tag2[u]==1?max(mx_,dep):mx_);
    check_mn_zzp((u<<1)+1,dep-1,tag2[u]==2?max(mx_,dep):mx_);
    return ;
}

void check_cl_zzp(int u,int dep){
  //第一类的标记
    if (dep<0) return;
    tag[u]=-1;
    check_cl_zzp((u<<1),dep-1);
    check_cl_zzp((u<<1)+1,dep-1);
    return ;
}

void check(int u,short dep){
  //暴力跳关键边
    if (dep==T1) return ;
    if (tag[u]==0) return ;
    if (tag[u]==1){
        if (!(u&1)){
            if (a[w[u]]<=dep){
                tag[u+1]=2;
                if (!flag[u]) cur-=w[u];
                return ;
            }else{
                if (!flag[u+1]) cur-=w[u+1]; 
                check_cl_zzp(u+1,dep);
                w[(u>>1)]=w[u]; flag[(u>>1)]=flag[u];
                check((u>>1),dep+1);
                return ;
            }
        }else{
            if (a[w[u]]<=dep){
                tag[u-1]=2;
                check(u-1,dep);
                return ;
            }else{
                if (!flag[u-1]) cur-=w[u-1]; 
                w[(u>>1)]=w[u]; flag[(u>>1)]=flag[u];
                check((u>>1),dep+1);
                return ;
            }
        }
    }
    if (tag[u]==2){
        w[(u>>1)]=w[u]; flag[(u>>1)]=flag[u];
        check((u>>1),dep+1);
        return ;
    }
}

void Solve(){
    T1=0;
    cur=1; ans[1]=1; //cur 是当前答案,ans 记录答案
    for (int i=2;i<=n;i++){
        if ((1<<T1)<i){
            T1++;
            check_mn_zzp(1<<zzp-T1,T1,0);
            cur+=w[(1<<zzp-T1+1)+1];
            check(1<<zzp-T1+1,T1-1);
        }
        if (mn[(1<<zzp)+i-1]>a[i]&&tag[(1<<zzp)+i-1]!=-1) cur-=i, flag[(1<<zzp)+i-1]=1;//flag 是能力值限制的标记
        check((1<<zzp)+i-1,0);
        ans[i]=cur;
    }
    long long Ans=0;
    for (int i=1;i<=m;i++)
        Ans^=(i*ans[b[i]]);
    write_(Ans,'\n');
}

signed main(){
    n=read(); m=read();
    while ((1<<zzp)<n) zzp++;
    for (int i=1;i<=n;i++) a_[i]=read();
    for (int i=1;i<=m;i++) b[i]=read();
    for (int i=zzp-1;i>=0;i--) scanf("%s",s+(1<<zzp)-(1<<i+1)+1);
    int t; t=read();
    while (t--){
        zzpx[0]=read(); zzpx[1]=read(); zzpx[2]=read(); zzpx[3]=read();
        Init(); Solve();
    }
}