题目E:有个队伍进行比赛,编号为1,2,.....
,要打
场比赛,第一轮编号为奇数的队伍和与它相邻的下一个队伍进行比赛,比赛胜出的
组队伍进行下一轮比赛,之后1vs2胜出的队伍与3vs4胜出的队伍进行比赛,以此类推。然后胜出的队伍继续进行比赛,直到只剩下一组队伍。之后给队伍排名次,第i个小队的名次为pi。比如k为3,则有8个队伍比赛,第一轮比赛为1vs2,3vs4,5vs6,7vs8。若2、4、5、8胜出,则第二轮为2vs4,5vs8,若4、5胜出,则第三轮4vs5。若5胜出,则1、3、6、7名次为5,2、8为3,4为2,5为1。
定义,mod=998244354。输入k,A,h,输出各小组排名,如果无法找出合理排名输出-1。
题解:对于每一场比赛,胜负状态有两种,分别为编号小的胜利和编号大的胜利。因此可以用一个位二进制数表示每场比赛的状况,共
种比赛状态。可以根据比赛的状况求出每个小队的排名,并求出
,若
=h则输出并且返回。 如果k=5,这样做不能通过此题。此时需要进行双向搜索。当k=5时第一名编号可能在1到
之间,或者在
到
之间。进行分情况讨论。对于每种情况对编号在前半部分的队伍枚举
种比赛状态,对每种状态的
值进行标记。然后计算后半部分每种比赛状态的
值。如果
有标记,则说明该种情况可行。
#include<bits/stdc++.h>
using namespace std;
const int MOD=998244353;
int add(int x,int y) {//加法取模
x+=y;
while(x>=MOD)x-=MOD;
while(x<0)x+=MOD;
return x;
}
int mul(int x,int y) {//乘法取模
return (x*1ll*y)%MOD;
}
int binpow(int x,int y) {//x^y取模
int z=1;
while(y) {
if(y&1)z=mul(z,x);
x=mul(x,x);
y>>=1;
}
return z;
}
vector<int> evaluate(int n,int choice_mask) {//将状态转化为名次
int cur_place=n/2+1;
int cur_bit=n-2;
vector<int> p(n);
vector<int> c(n);
for(int i=0; i<n; i++)c[i]=i;
while(c.size()!=1) {
vector<int> nc;
for(int i=0; i<c.size(); i+=2) {
if(choice_mask&(1<<cur_bit)) {
p[c[i]]=cur_place;
nc.push_back(c[i+1]);
} else {
p[c[i+1]]=cur_place;
nc.push_back(c[i]);
}
cur_bit--;
}
c=nc;
cur_place/=2;
cur_place++;
}
p[c[0]]=1;
return p;
}
vector<int> adjust(int n,vector<int> p,bool winning) {//调整名次
for(int i=0; i<n; i++) {
if(p[i]==1) {
if(!winning)p[i]++;
} else p[i]=p[i]*2-1;
}
return p;
}
int get_hash(int n,vector<int> p,int A,bool partial=false,bool winning=false,int shift=0) {//获得对应的h
if(partial)p=adjust(n,p,winning);
int res=0;
for(int i=0; i<n; i++)
res=add(res,mul(add(i+1,shift),binpow(A,p[i])));
return res;
}
int main() {
int k,A,h;
cin>>k>>A>>h;
if(k<=4) {//k<=4直接暴力
for(int i=0; i<(1<<((1<<k)-1)); i++) {//每一个i代表一种对局状态
vector<int> p=evaluate(1<<k,i);
if(get_hash(1<<k,p,A)==h) {
for(auto x:p)cout<<x<<" ";
cout<<endl;
return 0;
}
}
} else {
int mask_left=-1;
int mask_right=-1;
bool left_win=false;
for(int c=0; c<2; c++) {//c==0前半部分产生第一,c==1后半部分产生第一
map<int,int> left_map;
for(int i=0; i<(1<<16); i++) {//将前半部分得分存在left_map中
vector<int> p=evaluate(16,i);
int left_hash=get_hash(16,p,A,true,c==0,0);
left_map[left_hash]=i;
}
for(int i=0; i<(1<<16); i++) {//计算后半部分哈希值并且查询left_map中是否存在使得答案正确的前半部分的排列。
vector<int> p=evaluate(16,i);
int right_hash=get_hash(16,p,A,true,c==1,16);
int left_hash=add(h,MOD-right_hash);
if(left_map.count(left_hash)) {
mask_left=left_map[left_hash];
mask_right=i;
left_win=(c==0);
}
}
}
if(mask_left!=-1) {
vector<int> ans_left=evaluate(16,mask_left);
vector<int> ans_right=evaluate(16,mask_right);
ans_left=adjust(16,ans_left,left_win);
ans_right=adjust(16,ans_right,!left_win);
for(auto x:ans_left)cout<<x<<" ";
for(auto x:ans_right)cout<<x<<" ";
return 0;
}
}
cout<<-1<<endl;
return 0;
}