做法简述:在从 1 到 n 枚举每个前缀确定时,考虑每个节点维护可能答案编号和,初始化时平凡的,记录可能的自由选手组成的胜者编号最小的。
对于编号和,由于我们只关心最终答案,这个不一定表示节点位置的编号和,而表示有用的编号和,对于胜者确定的点,这个编号和为当前节点真实的,否则不一定真实,但一定为到后面不会被因为能力值卡掉的点,每个点预处理哪里被卡掉。然后暴力从叶子向跟转移,每次转移有三种情况,两个儿子都确定(平凡),一个确定(如果必须用那个儿子就平凡,否则考虑那个儿子之后是否会因为能力值卡掉,然后是否添加,右边肯定添加),或者都没确定,直接加和即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int gg;
int aa[100009];
int c[100009];
int n,m;
int k;
bool d[21][200009];
int a[100009];
int x[4];
long long ans[100009];
int hz[21][200009];
long long sm[21][200009];
bool ok[100009][21];
inline void inite(){
cin>>x[0]>>x[1]>>x[2]>>x[3];
for(int i=1;i<=n;i++){
a[i]=(aa[i]^x[i%4]);
ans[i-1]=0;
}
int k;
k=0;
while((1ll<<k)<n){
k++;
}
for(int i=0;i<=k;i++){
for(int j=0;j<=n;j++){
ok[j][i]=0;
}
}
for(int i=0;i<=k;i++){
for(int j=0;j<(1<<(k-i));j++){
if(!i){
sm[i][j]=j+1;hz[i][j]=j;
}else{
sm[i][j]=sm[i-1][j<<1]+sm[i-1][j<<1|1];hz[i][j]=hz[i-1][j<<1];
}
}
}
}
inline void prk(){
long long anss;
anss=0;
for(int i=1;i<=m;i++){
anss^=(ans[c[i]]*i);
}
cout<<anss<<endl;
}
inline void sc(int x,int y){
int l,r;
l=(y<<1);
r=((y<<1)|1);
if(hz[x-1][l]==-1&&hz[x-1][r]==-1){
hz[x][y]=-1;
if(d[x][y]){
if(ok[sm[x-1][r]][x]){
sm[x][y]=sm[x-1][r];
}else{
sm[x][y]=sm[x-1][l];
}
}else{
if(ok[sm[x-1][l]][x]){
sm[x][y]=sm[x-1][l];
}else{
sm[x][y]=sm[x-1][r];
}
}
return;
}
if(hz[x-1][l]==-1){
sm[x][y]=0;
if(d[x][y]){
hz[x][y]=hz[x-1][r];
sm[x][y]+=sm[x-1][r];
if(ok[sm[x-1][l]][gg]){
sm[x][y]+=sm[x-1][l];
}
}else{
if(!ok[sm[x-1][l]][x]){
hz[x][y]=hz[x-1][r];
sm[x][y]+=sm[x-1][r];
}else{
sm[x][y]+=sm[x-1][l];
hz[x][y]=-1;
}
}
return;
}
sm[x][y]=0;
hz[x][y]=hz[x-1][l];
sm[x][y]+=sm[x-1][r]+sm[x-1][l];
}
inline void _main(){
inite();
int k;
k=0;
while((1<<k)<n){
k++;
}
gg=0;
for(int i=0;i<n;i++){
int z;
z=i;
ok[i+1][0]=0;
for(int j=1;j<=k;j++){
bool g;
g=(z&1);
z>>=1;
if(g==d[j][z]&&a[i+1]<j){
ok[i+1][j]=0;
break;
}else{
ok[i+1][j]=1;
}
}
}
for(int i=1;i<=n;i++){
while((1<<gg)<i){
gg++;
}
//cout<<i<<"-"<<gg<<endl;
int z;
z=i-1;
int g;
g=0;
sm[g][z]=i;
hz[g][z]=-1;
while(z){
z>>=1;
g++;
sc(g,z);
}
//cout<<sm[gg][0]<<endl;
ans[i]=sm[gg][0];
}
prk();
}
signed main(){
std::ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>aa[i];
}
for(int i=1;i<=m;i++){
cin>>c[i];
}
int k;
k=0;
while((1<<k)<n){
k++;
}
for(int c=1;c<=k;c++){
for(int j=0;j<(1<<(k-c));j++){
char t;
cin>>t;
d[c][j]=t-'0';
}
}
int t;
cin>>t;
while(t--){
_main();
}
return 0;
}
80 分,我要给它完整的一生。